Blame view

drivers/hwmon/smsc47m1.c 24.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   * 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>
   * Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
   * 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

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

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
154
  static struct platform_driver smsc47m1_driver = {
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
155
  	.driver = {
872188420   Jean Delvare   i2c-isa: Restore ...
156
  		.owner	= THIS_MODULE,
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
157
  		.name	= DRVNAME,
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
158
  	},
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
159
  	.remove		= __exit_p(smsc47m1_remove),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  };
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
161
162
  static ssize_t get_fan(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
164
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
166
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
167
168
169
170
171
172
  	/*
  	 * 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
173
174
175
176
177
178
179
  	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...
180
181
  static ssize_t get_fan_min(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
183
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
185
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
  	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...
191
192
  static ssize_t get_fan_div(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
194
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
196
197
  	return sprintf(buf, "%d
  ", DIV_FROM_REG(data->fan_div[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  }
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
199
200
201
202
203
204
205
206
  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...
207
208
  static ssize_t get_pwm(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
210
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
212
213
  	return sprintf(buf, "%d
  ", PWM_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
215
216
  static ssize_t get_pwm_en(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
218
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
220
221
  	return sprintf(buf, "%d
  ", PWM_EN_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
223
224
  static ssize_t get_alarms(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
  {
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
  	return sprintf(buf, "%d
  ", data->alarms);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
230
231
  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
232
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
233
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
234
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
235
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
236
237
238
239
240
241
242
  	long rpmdiv;
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
244
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
  	rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
  
  	if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
248
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
  		return -EINVAL;
  	}
  
  	data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
253
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
255
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
  
  	return count;
  }
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
259
260
261
262
263
264
  /*
   * 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...
265
266
  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
267
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
268
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
269
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
270
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
271
272
273
  	long new_div;
  	int err;
  	long tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  	u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
275
276
277
  	err = kstrtol(buf, 10, &new_div);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
  	if (new_div == old_div) /* No change */
  		return count;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
280
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  	switch (new_div) {
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
282
283
284
285
286
287
288
289
290
291
292
293
  	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
294
  	default:
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
295
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
  		return -EINVAL;
  	}
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
298
299
300
  	switch (nr) {
  	case 0:
  	case 1:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
301
  		tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
302
303
  		      & ~(0x03 << (4 + 2 * nr));
  		tmp |= data->fan_div[nr] << (4 + 2 * nr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
304
  		smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
305
306
  		break;
  	case 2:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
307
  		tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
308
  		tmp |= data->fan_div[2] << 4;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
309
  		smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
310
311
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
  
  	/* Preserve fan min */
  	tmp = 192 - (old_div * (192 - data->fan_preload[nr])
  		     + new_div / 2) / new_div;
2a844c148   Guenter Roeck   hwmon: Replace SE...
316
  	data->fan_preload[nr] = clamp_val(tmp, 0, 191);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
317
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
319
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
323
324
  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
325
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
326
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
327
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
328
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
329
330
331
332
333
334
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
  
  	if (val < 0 || val > 255)
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
338
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
  	data->pwm[nr] &= 0x81; /* Preserve additional bits */
  	data->pwm[nr] |= PWM_TO_REG(val);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
341
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
343
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
347
348
  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
349
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
350
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
351
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
352
  	int nr = attr->index;
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
353
354
355
356
357
358
359
360
  	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
361
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
362
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  	data->pwm[nr] &= 0xFE; /* preserve the other bits */
  	data->pwm[nr] |= !val;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
365
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
367
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
  
  	return count;
  }
  
  #define fan_present(offset)						\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
373
374
375
376
377
378
  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)...
379
380
  static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm,	\
  		NULL, offset - 1);					\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
381
382
383
384
  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
385
386
387
  
  fan_present(1);
  fan_present(2);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
388
  fan_present(3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  
  static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
391
392
393
394
395
396
397
398
399
  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)...
400
  static struct attribute *smsc47m1_attributes_fan1[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
401
402
403
  	&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)...
404
  	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
405
406
407
408
409
410
411
412
  	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...
413
414
415
  	&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)...
416
  	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
417
418
419
420
421
422
423
424
  	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...
425
426
427
  	&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)...
428
  	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
429
430
  	NULL
  };
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
431

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
432
433
434
435
436
  static const struct attribute_group smsc47m1_group_fan3 = {
  	.attrs = smsc47m1_attributes_fan3,
  };
  
  static struct attribute *smsc47m1_attributes_pwm1[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
437
438
  	&sensor_dev_attr_pwm1.dev_attr.attr,
  	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
439
440
441
442
443
444
445
446
  	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...
447
448
  	&sensor_dev_attr_pwm2.dev_attr.attr,
  	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
449
450
451
452
453
454
455
456
  	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...
457
458
  	&sensor_dev_attr_pwm3.dev_attr.attr,
  	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
459
460
  	NULL
  };
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
461

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
462
463
464
465
466
  static const struct attribute_group smsc47m1_group_pwm3 = {
  	.attrs = smsc47m1_attributes_pwm3,
  };
  
  static struct attribute *smsc47m1_attributes[] = {
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
467
  	&dev_attr_alarms.attr,
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
468
  	&dev_attr_name.attr,
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
469
470
471
472
473
474
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group = {
  	.attrs = smsc47m1_attributes,
  };
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
475
  static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
  {
  	u8 val;
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
478
  	unsigned short addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
  
  	superio_enter();
67b671bce   Jean Delvare   hwmon: Let the us...
481
  	val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
  
  	/*
6091780eb   Jean Delvare   smsc47m1: List th...
484
485
  	 * 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
486
  	 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
ec5ce552d   Jean Delvare   [PATCH] I2C: Add ...
487
  	 * can do much more besides (device id 0x60).
b890a07f7   Jean Delvare   [PATCH] hwmon: sm...
488
489
  	 * The LPC47M997 is undocumented, but seems to be compatible with
  	 * the LPC47M192, and has the same device id.
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
490
491
492
  	 * 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)...
493
494
495
  	 * 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
496
  	 */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
497
  	switch (val) {
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
498
  	case 0x51:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
499
500
  		pr_info("Found SMSC LPC47B27x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
501
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
502
503
  		break;
  	case 0x59:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
504
505
  		pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
506
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
507
508
  		break;
  	case 0x5F:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
509
510
  		pr_info("Found SMSC LPC47M14x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
511
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
512
513
  		break;
  	case 0x60:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
514
515
  		pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
516
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
517
518
  		break;
  	case 0x6B:
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
519
  		if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
520
521
  			pr_debug("Found SMSC LPC47M233, unsupported
  ");
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
522
523
524
  			superio_exit();
  			return -ENODEV;
  		}
512504e9f   Joe Perches   hwmon: (smsc47m1)...
525
526
  		pr_info("Found SMSC LPC47M292
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
527
  		sio_data->type = smsc47m2;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
528
529
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
533
534
  		superio_exit();
  		return -ENODEV;
  	}
  
  	superio_select();
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
535
  	addr = (superio_inb(SUPERIO_REG_BASE) << 8)
2d8672c5a   Jean Delvare   [PATCH] I2C: Sepa...
536
  	      |  superio_inb(SUPERIO_REG_BASE + 1);
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
537
  	if (addr == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
538
539
  		pr_info("Device address not set, will not use
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
  		superio_exit();
  		return -ENODEV;
  	}
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
543
544
545
546
  	/*
  	 * Enable only if address is set (needed at least on the
  	 * Compaq Presario S4000NX)
  	 */
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
547
548
  	sio_data->activate = superio_inb(SUPERIO_REG_ACT);
  	if ((sio_data->activate & 0x01) == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
549
550
  		pr_info("Enabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
551
552
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  	superio_exit();
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
554
  	return addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  }
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
556
  /* Restore device to its initial state */
a00d643a2   Jeff Mahoney   hwmon: (smsc47m1)...
557
  static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
558
559
560
561
  {
  	if ((sio_data->activate & 0x01) == 0) {
  		superio_enter();
  		superio_select();
512504e9f   Joe Perches   hwmon: (smsc47m1)...
562
563
  		pr_info("Disabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
564
565
566
567
568
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate);
  
  		superio_exit();
  	}
  }
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
569
570
  #define CHECK		1
  #define REQUEST		2
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
571
572
573
574
575
  
  /*
   * This function can be used to:
   *  - test for resource conflicts with ACPI
   *  - request the resources
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
576
577
578
   * 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)...
579
580
581
  static int __init smsc47m1_handle_resources(unsigned short address,
  					    enum chips type, int action,
  					    struct device *dev)
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  {
  	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)...
628
629
630
631
632
  			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)...
633
634
635
  				return -EBUSY;
  			}
  			break;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
636
637
638
639
640
  		}
  	}
  
  	return 0;
  }
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
641
642
643
644
645
646
647
648
649
650
  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)...
651
  static int __init smsc47m1_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
653
654
  	struct device *dev = &pdev->dev;
  	struct smsc47m1_sio_data *sio_data = dev->platform_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  	struct smsc47m1_data *data;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
656
  	struct resource *res;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
657
  	int err;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
658
  	int fan1, fan2, fan3, pwm1, pwm2, pwm3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659

85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
660
  	static const char * const names[] = {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
661
662
663
664
665
  		"smsc47m1",
  		"smsc47m2",
  	};
  
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
666
667
668
669
  	err = smsc47m1_handle_resources(res->start, sio_data->type,
  					REQUEST, dev);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670

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

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
675
676
677
  	data->addr = res->start;
  	data->type = sio_data->type;
  	data->name = names[sio_data->type];
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
678
  	mutex_init(&data->update_lock);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
679
  	platform_set_drvdata(pdev, data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680

85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
681
682
683
684
  	/*
  	 * If no function is properly configured, there's no point in
  	 * actually registering the chip.
  	 */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
685
  	pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  	       == 0x04;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
687
  	pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  	       == 0x04;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
689
  	if (data->type == smsc47m2) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
690
  		fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
691
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
692
  		fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
693
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
694
  		fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
695
  			& 0x0d) == 0x0d;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
696
  		pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
697
698
  			& 0x0d) == 0x08;
  	} else {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
699
  		fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
700
  			& 0x05) == 0x05;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
701
  		fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
702
703
704
705
706
  			& 0x05) == 0x05;
  		fan3 = 0;
  		pwm3 = 0;
  	}
  	if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
707
708
  		dev_warn(dev, "Device not configured, will not use
  ");
08ad7c952   Guenter Roeck   hwmon: (smsc47m1)...
709
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  	}
85a0c0d1a   Guenter Roeck   hwmon: (smsc47m1)...
711
712
713
714
715
716
717
718
  	/*
  	 * 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...
719
  	smsc47m1_update_device(dev, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720

943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
721
  	/* Register sysfs hooks */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  	if (fan1) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
723
724
725
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan1);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
726
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
728
729
  		dev_dbg(dev, "Fan 1 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  
  	if (fan2) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
732
733
734
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan2);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
735
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
737
738
  		dev_dbg(dev, "Fan 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
740
  	if (fan3) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
741
742
743
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_fan3);
  		if (err)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
744
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
745
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
746
747
  		dev_dbg(dev, "Fan 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
748

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  	if (pwm1) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
750
751
752
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm1);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
753
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
755
756
  		dev_dbg(dev, "PWM 1 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
757

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
  	if (pwm2) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
759
760
761
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm2);
  		if (err)
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
762
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
764
765
  		dev_dbg(dev, "PWM 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
767
  	if (pwm3) {
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
768
769
770
  		err = sysfs_create_group(&dev->kobj,
  					 &smsc47m1_group_pwm3);
  		if (err)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
771
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
772
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
773
774
  		dev_dbg(dev, "PWM 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
775

7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
776
777
  	err = sysfs_create_group(&dev->kobj, &smsc47m1_group);
  	if (err)
68a50b567   Jean Delvare   hwmon: (smsc47m1)...
778
  		goto error_remove_files;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
779

1beeffe43   Tony Jones   hwmon: Convert fr...
780
781
782
  	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...
783
784
  		goto error_remove_files;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
  
  	return 0;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
787
  error_remove_files:
7e612685a   Guenter Roeck   hwmon: (smsc47m1)...
788
  	smsc47m1_remove_files(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
790
  	return err;
  }
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
791
  static int __exit smsc47m1_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
793
  	struct smsc47m1_data *data = platform_get_drvdata(pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
  static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
  		int init)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
803
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804

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

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
811
  		for (i = 0; i < fan_nr; i++) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
812
  			data->fan[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
813
  				       SMSC47M1_REG_FAN[i]);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
814
  			data->fan_preload[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
815
  					       SMSC47M1_REG_FAN_PRELOAD[i]);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
816
  			data->pwm[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
817
  				       SMSC47M1_REG_PWM[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
  		}
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
819
  		i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
  		data->fan_div[0] = (i >> 4) & 0x03;
  		data->fan_div[1] = i >> 6;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
822
  		data->alarms = smsc47m1_read_value(data,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
824
825
  			       SMSC47M1_REG_ALARM) >> 6;
  		/* Clear alarms if needed */
  		if (data->alarms)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
826
  			smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
828
  		if (fan_nr >= 3) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
829
  			data->fan_div[2] = (smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
830
  					    SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
831
  			data->alarms |= (smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
832
833
834
  					 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
  			/* Clear alarm if needed */
  			if (data->alarms & 0x04)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
835
  				smsc47m1_write_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
836
837
838
  						     SMSC47M2_REG_ALARM6,
  						     0x40);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
  		data->last_updated = jiffies;
  	}
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
841
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
  	return data;
  }
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
844
845
846
847
848
849
850
851
852
853
  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)...
854
  	err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
b9acb64a3   Jean Delvare   hwmon: Check for ...
855
856
  	if (err)
  		goto exit;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
857
858
859
  	pdev = platform_device_alloc(DRVNAME, address);
  	if (!pdev) {
  		err = -ENOMEM;
512504e9f   Joe Perches   hwmon: (smsc47m1)...
860
861
  		pr_err("Device allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
862
863
864
865
866
  		goto exit;
  	}
  
  	err = platform_device_add_resources(pdev, &res, 1);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
867
868
  		pr_err("Device resource addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
869
870
  		goto exit_device_put;
  	}
2df6d8115   Jean Delvare   hwmon: Use platfo...
871
872
873
  	err = platform_device_add_data(pdev, sio_data,
  				       sizeof(struct smsc47m1_sio_data));
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
874
875
  		pr_err("Platform data allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
876
877
  		goto exit_device_put;
  	}
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
878
879
880
  
  	err = platform_device_add(pdev);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
881
882
  		pr_err("Device addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
883
884
885
886
887
888
889
890
891
892
  		goto exit_device_put;
  	}
  
  	return 0;
  
  exit_device_put:
  	platform_device_put(pdev);
  exit:
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
  static int __init sm_smsc47m1_init(void)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
895
896
897
  	int err;
  	unsigned short address;
  	struct smsc47m1_sio_data sio_data;
1d0045ee4   Guenter Roeck   hwmon: (smsc47m1)...
898
899
900
901
  	err = smsc47m1_find(&sio_data);
  	if (err < 0)
  		return err;
  	address = err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902

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

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