Blame view

drivers/hwmon/smsc47m1.c 24.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
2
3
4
5
6
7
8
9
   * smsc47m1.c - Part of lm_sensors, Linux kernel modules
   *		for hardware monitoring
   *
   * Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
   * LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
   * Super-I/O chips.
   *
   * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
7c81c60f3   Jean Delvare   Update Jean Delva...
10
   * Copyright (C) 2004-2007 Jean Delvare <jdelvare@suse.de>
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   * 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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

512504e9f   Joe Perches   hwmon: (smsc47m1)...
29
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/ioport.h>
  #include <linux/jiffies.h>
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
34
  #include <linux/platform_device.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
35
  #include <linux/hwmon.h>
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
36
  #include <linux/hwmon-sysfs.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
37
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  #include <linux/init.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
39
  #include <linux/mutex.h>
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
40
  #include <linux/sysfs.h>
b9acb64a3   Jean Delvare   hwmon: Check for ...
41
  #include <linux/acpi.h>
6055fae8a   H Hartley Sweeten   hwmon: Include <l...
42
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

67b671bce   Jean Delvare   hwmon: Let the us...
44
45
46
  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...
47
48
49
  static struct platform_device *pdev;
  
  #define DRVNAME "smsc47m1"
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
50
  enum chips { smsc47m1, smsc47m2 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
  
  /* Super-I/0 registers and commands */
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
53
54
  #define REG	0x2e	/* The register to read/write */
  #define VAL	0x2f	/* The value to read/write */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
87
  
  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)...
88
  #define SUPERIO_REG_DEVREV	0x21
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
95
96
97
  
  /* 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
98
  #define SMSC47M1_REG_FANDIV		0x58
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
99
100
101
102
103
104
105
106
107
108
109
  
  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
110

85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
111
112
113
114
115
  #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)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
  #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...
122
123
  	unsigned short addr;
  	const char *name;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
124
  	enum chips type;
1beeffe43   Tony Jones   hwmon: Convert fr...
125
  	struct device *hwmon_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126

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

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

e70198acf   Axel Lin   hwmon: (smsc47m1)...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
  		int init)
  {
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
  
  	mutex_lock(&data->update_lock);
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
  		int i, fan_nr;
  		fan_nr = data->type == smsc47m2 ? 3 : 2;
  
  		for (i = 0; i < fan_nr; i++) {
  			data->fan[i] = smsc47m1_read_value(data,
  				       SMSC47M1_REG_FAN[i]);
  			data->fan_preload[i] = smsc47m1_read_value(data,
  					       SMSC47M1_REG_FAN_PRELOAD[i]);
  			data->pwm[i] = smsc47m1_read_value(data,
  				       SMSC47M1_REG_PWM[i]);
  		}
  
  		i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
  		data->fan_div[0] = (i >> 4) & 0x03;
  		data->fan_div[1] = i >> 6;
  
  		data->alarms = smsc47m1_read_value(data,
  			       SMSC47M1_REG_ALARM) >> 6;
  		/* Clear alarms if needed */
  		if (data->alarms)
  			smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
  
  		if (fan_nr >= 3) {
  			data->fan_div[2] = (smsc47m1_read_value(data,
  					    SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
  			data->alarms |= (smsc47m1_read_value(data,
  					 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
  			/* Clear alarm if needed */
  			if (data->alarms & 0x04)
  				smsc47m1_write_value(data,
  						     SMSC47M2_REG_ALARM6,
  						     0x40);
  		}
  
  		data->last_updated = jiffies;
  	}
  
  	mutex_unlock(&data->update_lock);
  	return data;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198

e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
199
200
  static ssize_t get_fan(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
202
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
204
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
205
206
207
208
209
210
  	/*
  	 * 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).
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
  	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...
218
219
  static ssize_t get_fan_min(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
221
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
223
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
  	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...
229
230
  static ssize_t get_fan_div(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
232
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
234
235
  	return sprintf(buf, "%d
  ", DIV_FROM_REG(data->fan_div[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  }
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
237
238
239
240
241
242
243
244
  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...
245
246
  static ssize_t get_pwm(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
248
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
250
251
  	return sprintf(buf, "%d
  ", PWM_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
253
254
  static ssize_t get_pwm_en(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
258
259
  	return sprintf(buf, "%d
  ", PWM_EN_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
261
262
  static ssize_t get_alarms(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
  {
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
  	return sprintf(buf, "%d
  ", data->alarms);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
268
269
  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
270
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
271
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
272
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
273
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
274
275
276
277
278
279
280
  	long rpmdiv;
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
282
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
  	rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
  
  	if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
286
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
  		return -EINVAL;
  	}
  
  	data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
291
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
293
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
  
  	return count;
  }
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
297
298
299
300
301
302
  /*
   * 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
   * of least surprise; the user doesn't expect the fan minimum to change just
   * because the divider changed.
   */
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
303
304
  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
305
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
306
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
307
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
308
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
309
310
311
  	long new_div;
  	int err;
  	long tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  	u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
313
314
315
  	err = kstrtol(buf, 10, &new_div);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
  	if (new_div == old_div) /* No change */
  		return count;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
318
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  	switch (new_div) {
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
320
321
322
323
324
325
326
327
328
329
330
331
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  	default:
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
333
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  		return -EINVAL;
  	}
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
336
337
338
  	switch (nr) {
  	case 0:
  	case 1:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
339
  		tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
340
341
  		      & ~(0x03 << (4 + 2 * nr));
  		tmp |= data->fan_div[nr] << (4 + 2 * nr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
342
  		smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
343
344
  		break;
  	case 2:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
345
  		tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
346
  		tmp |= data->fan_div[2] << 4;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
347
  		smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
348
349
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
  
  	/* Preserve fan min */
  	tmp = 192 - (old_div * (192 - data->fan_preload[nr])
  		     + new_div / 2) / new_div;
2a844c148   Guenter Roeck   hwmon: Replace SE...
354
  	data->fan_preload[nr] = clamp_val(tmp, 0, 191);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
355
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
357
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
361
362
  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
363
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
364
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
365
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
366
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
367
368
369
370
371
372
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
  
  	if (val < 0 || val > 255)
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
376
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
  	data->pwm[nr] &= 0x81; /* Preserve additional bits */
  	data->pwm[nr] |= PWM_TO_REG(val);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
379
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
381
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
385
386
  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
387
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
388
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
389
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
390
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
391
392
393
394
395
396
397
398
  	unsigned long val;
  	int err;
  
  	err = kstrtoul(buf, 10, &val);
  	if (err)
  		return err;
  
  	if (val > 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
400
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
  	data->pwm[nr] &= 0xFE; /* preserve the other bits */
  	data->pwm[nr] |= !val;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
403
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
405
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
  
  	return count;
  }
  
  #define fan_present(offset)						\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
411
412
413
414
415
416
  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)...
417
418
  static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm,	\
  		NULL, offset - 1);					\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
419
420
421
422
  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
423
424
425
  
  fan_present(1);
  fan_present(2);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
426
  fan_present(3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
  
  static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
429
430
431
432
433
434
435
436
437
  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);
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
438
  static struct attribute *smsc47m1_attributes_fan1[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
439
440
441
  	&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)...
442
  	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
443
444
445
446
447
448
449
450
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group_fan1 = {
  	.attrs = smsc47m1_attributes_fan1,
  };
  
  static struct attribute *smsc47m1_attributes_fan2[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
451
452
453
  	&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)...
454
  	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
455
456
457
458
459
460
461
462
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group_fan2 = {
  	.attrs = smsc47m1_attributes_fan2,
  };
  
  static struct attribute *smsc47m1_attributes_fan3[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
463
464
465
  	&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)...
466
  	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
467
468
  	NULL
  };
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
469

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
470
471
472
473
474
  static const struct attribute_group smsc47m1_group_fan3 = {
  	.attrs = smsc47m1_attributes_fan3,
  };
  
  static struct attribute *smsc47m1_attributes_pwm1[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
475
476
  	&sensor_dev_attr_pwm1.dev_attr.attr,
  	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
477
478
479
480
481
482
483
484
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group_pwm1 = {
  	.attrs = smsc47m1_attributes_pwm1,
  };
  
  static struct attribute *smsc47m1_attributes_pwm2[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
485
486
  	&sensor_dev_attr_pwm2.dev_attr.attr,
  	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
487
488
489
490
491
492
493
494
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group_pwm2 = {
  	.attrs = smsc47m1_attributes_pwm2,
  };
  
  static struct attribute *smsc47m1_attributes_pwm3[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
495
496
  	&sensor_dev_attr_pwm3.dev_attr.attr,
  	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
497
498
  	NULL
  };
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
499

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
500
501
502
503
504
  static const struct attribute_group smsc47m1_group_pwm3 = {
  	.attrs = smsc47m1_attributes_pwm3,
  };
  
  static struct attribute *smsc47m1_attributes[] = {
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
505
  	&dev_attr_alarms.attr,
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
506
  	&dev_attr_name.attr,
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
507
508
509
510
511
512
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group = {
  	.attrs = smsc47m1_attributes,
  };
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
513
  static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
  {
  	u8 val;
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
516
  	unsigned short addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
  
  	superio_enter();
67b671bce   Jean Delvare   hwmon: Let the us...
519
  	val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
  
  	/*
6091780eb   Jean Delvare   smsc47m1: List th...
522
523
  	 * 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
524
  	 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
ec5ce552d   Jean Delvare   [PATCH] I2C: Add ...
525
  	 * can do much more besides (device id 0x60).
b890a07f7   Jean Delvare   [PATCH] hwmon: sm...
526
527
  	 * The LPC47M997 is undocumented, but seems to be compatible with
  	 * the LPC47M192, and has the same device id.
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
528
529
530
  	 * 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)...
531
532
533
  	 * 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
534
  	 */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
535
  	switch (val) {
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
536
  	case 0x51:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
537
538
  		pr_info("Found SMSC LPC47B27x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
539
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
540
541
  		break;
  	case 0x59:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
542
543
  		pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
544
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
545
546
  		break;
  	case 0x5F:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
547
548
  		pr_info("Found SMSC LPC47M14x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
549
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
550
551
  		break;
  	case 0x60:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
552
553
  		pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
554
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
555
556
  		break;
  	case 0x6B:
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
557
  		if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
558
559
  			pr_debug("Found SMSC LPC47M233, unsupported
  ");
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
560
561
562
  			superio_exit();
  			return -ENODEV;
  		}
512504e9f   Joe Perches   hwmon: (smsc47m1)...
563
564
  		pr_info("Found SMSC LPC47M292
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
565
  		sio_data->type = smsc47m2;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
566
567
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
  		superio_exit();
  		return -ENODEV;
  	}
  
  	superio_select();
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
573
  	addr = (superio_inb(SUPERIO_REG_BASE) << 8)
2d8672c5a   Jean Delvare   [PATCH] I2C: Sepa...
574
  	      |  superio_inb(SUPERIO_REG_BASE + 1);
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
575
  	if (addr == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
576
577
  		pr_info("Device address not set, will not use
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
  		superio_exit();
  		return -ENODEV;
  	}
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
581
582
583
584
  	/*
  	 * Enable only if address is set (needed at least on the
  	 * Compaq Presario S4000NX)
  	 */
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
585
586
  	sio_data->activate = superio_inb(SUPERIO_REG_ACT);
  	if ((sio_data->activate & 0x01) == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
587
588
  		pr_info("Enabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
589
590
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  	superio_exit();
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
592
  	return addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  }
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
594
  /* Restore device to its initial state */
a00d643a2   Jeff Mahoney   hwmon: (smsc47m1)...
595
  static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
596
597
598
599
  {
  	if ((sio_data->activate & 0x01) == 0) {
  		superio_enter();
  		superio_select();
512504e9f   Joe Perches   hwmon: (smsc47m1)...
600
601
  		pr_info("Disabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
602
603
604
605
606
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate);
  
  		superio_exit();
  	}
  }
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
607
608
  #define CHECK		1
  #define REQUEST		2
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
609
610
611
612
613
  
  /*
   * This function can be used to:
   *  - test for resource conflicts with ACPI
   *  - request the resources
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
614
615
616
   * We only allocate the I/O ports we really need, to minimize the risk of
   * conflicts with ACPI or with other drivers.
   */
08ad7c952   Guenter Roeck   hwmon: (smsc47m1)...
617
618
619
  static int __init smsc47m1_handle_resources(unsigned short address,
  					    enum chips type, int action,
  					    struct device *dev)
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
  {
  	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 */
08ad7c952   Guenter Roeck   hwmon: (smsc47m1)...
666
667
668
669
670
  			if (!devm_request_region(dev, start, len, DRVNAME)) {
  				dev_err(dev,
  					"Region 0x%hx-0x%hx already in use!
  ",
  					start, start + len);
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
671
672
673
  				return -EBUSY;
  			}
  			break;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
674
675
676
677
678
  		}
  	}
  
  	return 0;
  }
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
679
680
681
682
683
684
685
686
687
688
  static void smsc47m1_remove_files(struct device *dev)
  {
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan1);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan2);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan3);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm1);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm2);
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm3);
  }
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
689
  static int __init smsc47m1_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
691
  	struct device *dev = &pdev->dev;
a8b3a3a53   Jingoo Han   hwmon: use dev_ge...
692
  	struct smsc47m1_sio_data *sio_data = dev_get_platdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
  	struct smsc47m1_data *data;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
694
  	struct resource *res;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
695
  	int err;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
696
  	int fan1, fan2, fan3, pwm1, pwm2, pwm3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697

85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
698
  	static const char * const names[] = {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
699
700
701
702
703
  		"smsc47m1",
  		"smsc47m2",
  	};
  
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
704
705
706
707
  	err = smsc47m1_handle_resources(res->start, sio_data->type,
  					REQUEST, dev);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708

08ad7c952   Guenter Roeck   hwmon: (smsc47m1)...
709
710
711
  	data = devm_kzalloc(dev, sizeof(struct smsc47m1_data), GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
713
714
715
  	data->addr = res->start;
  	data->type = sio_data->type;
  	data->name = names[sio_data->type];
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
716
  	mutex_init(&data->update_lock);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
717
  	platform_set_drvdata(pdev, data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718

85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
719
720
721
722
  	/*
  	 * If no function is properly configured, there's no point in
  	 * actually registering the chip.
  	 */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
723
  	pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  	       == 0x04;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
725
  	pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  	       == 0x04;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
727
  	if (data->type == smsc47m2) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
728
  		fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
729
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
730
  		fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
731
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
732
  		fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
733
  			& 0x0d) == 0x0d;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
734
  		pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
735
736
  			& 0x0d) == 0x08;
  	} else {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
737
  		fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
738
  			& 0x05) == 0x05;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
739
  		fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
740
741
742
743
744
  			& 0x05) == 0x05;
  		fan3 = 0;
  		pwm3 = 0;
  	}
  	if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
745
746
  		dev_warn(dev, "Device not configured, will not use
  ");
08ad7c952   Guenter Roeck   hwmon: (smsc47m1)...
747
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
  	}
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
749
750
751
752
753
754
755
756
  	/*
  	 * 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...
757
  	smsc47m1_update_device(dev, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758

943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
759
  	/* Register sysfs hooks */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
  	if (fan1) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
761
762
763
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan1);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
764
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
766
767
  		dev_dbg(dev, "Fan 1 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
  
  	if (fan2) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
770
771
772
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan2);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
773
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
775
776
  		dev_dbg(dev, "Fan 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
778
  	if (fan3) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
779
780
781
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan3);
  		if (err)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
782
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
783
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
784
785
  		dev_dbg(dev, "Fan 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
786

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
  	if (pwm1) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
788
789
790
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm1);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
791
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
793
794
  		dev_dbg(dev, "PWM 1 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
795

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
  	if (pwm2) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
797
798
799
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm2);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
800
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
802
803
  		dev_dbg(dev, "PWM 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
805
  	if (pwm3) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
806
807
808
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm3);
  		if (err)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
809
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
810
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
811
812
  		dev_dbg(dev, "PWM 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
813

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
814
815
  	err = sysfs_create_group(&dev->kobj, &smsc47m1_group);
  	if (err)
68a50b567   Jean Delvare   hwmon: (smsc47m1)...
816
  		goto error_remove_files;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
817

1beeffe43   Tony Jones   hwmon: Convert fr...
818
819
820
  	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...
821
822
  		goto error_remove_files;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
824
  
  	return 0;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
825
  error_remove_files:
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
826
  	smsc47m1_remove_files(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
  	return err;
  }
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
829
  static int __exit smsc47m1_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
831
  	struct smsc47m1_data *data = platform_get_drvdata(pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832

1beeffe43   Tony Jones   hwmon: Convert fr...
833
  	hwmon_device_unregister(data->hwmon_dev);
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
834
  	smsc47m1_remove_files(&pdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
837
  	return 0;
  }
e70198acf   Axel Lin   hwmon: (smsc47m1)...
838
839
  static struct platform_driver smsc47m1_driver = {
  	.driver = {
e70198acf   Axel Lin   hwmon: (smsc47m1)...
840
841
842
843
  		.name	= DRVNAME,
  	},
  	.remove		= __exit_p(smsc47m1_remove),
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
845
846
847
848
849
850
851
852
853
854
  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)...
855
  	err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
b9acb64a3   Jean Delvare   hwmon: Check for ...
856
857
  	if (err)
  		goto exit;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
858
859
860
  	pdev = platform_device_alloc(DRVNAME, address);
  	if (!pdev) {
  		err = -ENOMEM;
512504e9f   Joe Perches   hwmon: (smsc47m1)...
861
862
  		pr_err("Device allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
863
864
865
866
867
  		goto exit;
  	}
  
  	err = platform_device_add_resources(pdev, &res, 1);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
868
869
  		pr_err("Device resource addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
870
871
  		goto exit_device_put;
  	}
2df6d8115   Jean Delvare   hwmon: Use platfo...
872
873
874
  	err = platform_device_add_data(pdev, sio_data,
  				       sizeof(struct smsc47m1_sio_data));
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
875
876
  		pr_err("Platform data allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
877
878
  		goto exit_device_put;
  	}
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
879
880
881
  
  	err = platform_device_add(pdev);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
882
883
  		pr_err("Device addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
884
885
886
887
888
889
890
891
892
893
  		goto exit_device_put;
  	}
  
  	return 0;
  
  exit_device_put:
  	platform_device_put(pdev);
  exit:
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
  static int __init sm_smsc47m1_init(void)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
896
897
898
  	int err;
  	unsigned short address;
  	struct smsc47m1_sio_data sio_data;
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
899
900
901
902
  	err = smsc47m1_find(&sio_data);
  	if (err < 0)
  		return err;
  	address = err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903

3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
904
905
  	/* Sets global pdev as a side effect */
  	err = smsc47m1_device_add(address, &sio_data);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
906
  	if (err)
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
907
  		return err;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
908

3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
909
  	err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
910
  	if (err)
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
911
  		goto exit_device;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
912
913
  
  	return 0;
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
914
915
  exit_device:
  	platform_device_unregister(pdev);
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
916
  	smsc47m1_restore(&sio_data);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
917
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
921
  }
  
  static void __exit sm_smsc47m1_exit(void)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
922
  	platform_driver_unregister(&smsc47m1_driver);
a8b3a3a53   Jingoo Han   hwmon: use dev_ge...
923
  	smsc47m1_restore(dev_get_platdata(&pdev->dev));
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
924
  	platform_device_unregister(pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
927
928
929
930
931
932
  }
  
  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);