Blame view

drivers/hwmon/lm78.c 29 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
      lm78.c - Part of lm_sensors, Linux kernel modules for hardware
               monitoring
      Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
5
      Copyright (c) 2007, 2011  Jean Delvare <khali@linux-fr.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  
      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.
  */
ce47da742   Joe Perches   hwmon: (lm78) Use...
21
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/jiffies.h>
  #include <linux/i2c.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
27
  #include <linux/hwmon.h>
19f673edd   Jean Delvare   [PATCH] hwmon: hw...
28
  #include <linux/hwmon-vid.h>
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
29
  #include <linux/hwmon-sysfs.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
30
  #include <linux/err.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
31
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
33
34
35
36
37
  #ifdef CONFIG_ISA
  #include <linux/platform_device.h>
  #include <linux/ioport.h>
  #include <linux/io.h>
  #endif
c40769fee   Jean Delvare   hwmon/lm78: No lo...
38

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  /* Addresses to scan */
25e9c86d5   Mark M. Hoffman   hwmon: normal_i2c...
40
41
  static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
  						0x2e, 0x2f, I2C_CLIENT_END };
e5e9f44c2   Jean Delvare   i2c: Drop I2C_CLI...
42
  enum chips { lm78, lm79 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  
  /* Many LM78 constants specified below */
  
  /* Length of ISA address segment */
  #define LM78_EXTENT 8
  
  /* Where are the ISA address/data registers relative to the base address */
  #define LM78_ADDR_REG_OFFSET 5
  #define LM78_DATA_REG_OFFSET 6
  
  /* The LM78 registers */
  #define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
  #define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
  #define LM78_REG_IN(nr) (0x20 + (nr))
  
  #define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
  #define LM78_REG_FAN(nr) (0x28 + (nr))
  
  #define LM78_REG_TEMP 0x27
  #define LM78_REG_TEMP_OVER 0x39
  #define LM78_REG_TEMP_HYST 0x3a
  
  #define LM78_REG_ALARM1 0x41
  #define LM78_REG_ALARM2 0x42
  
  #define LM78_REG_VID_FANDIV 0x47
  
  #define LM78_REG_CONFIG 0x40
  #define LM78_REG_CHIPID 0x49
  #define LM78_REG_I2C_ADDR 0x48
  
  
  /* Conversions. Rounding and limit checking is only done on the TO_REG 
     variants. */
  
  /* IN: mV, (0V to 4.08V)
     REG: 16mV/bit */
  static inline u8 IN_TO_REG(unsigned long val)
  {
  	unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
  	return (nval + 8) / 16;
  }
  #define IN_FROM_REG(val) ((val) *  16)
  
  static inline u8 FAN_TO_REG(long rpm, int div)
  {
  	if (rpm <= 0)
  		return 255;
  	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
  }
  
  static inline int FAN_FROM_REG(u8 val, int div)
  {
  	return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
  }
  
  /* TEMP: mC (-128C to +127C)
     REG: 1C/bit, two's complement */
  static inline s8 TEMP_TO_REG(int val)
  {
  	int nval = SENSORS_LIMIT(val, -128000, 127000) ;
  	return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
  }
  
  static inline int TEMP_FROM_REG(s8 val)
  {
  	return val * 1000;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  #define DIV_FROM_REG(val) (1 << (val))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  struct lm78_data {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
113
  	struct i2c_client *client;
1beeffe43   Tony Jones   hwmon: Convert fr...
114
  	struct device *hwmon_dev;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
115
  	struct mutex lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  	enum chips type;
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
117
118
119
  	/* For ISA device only */
  	const char *name;
  	int isa_addr;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
120
  	struct mutex update_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  	char valid;		/* !=0 if following fields are valid */
  	unsigned long last_updated;	/* In jiffies */
  
  	u8 in[7];		/* Register value */
  	u8 in_max[7];		/* Register value */
  	u8 in_min[7];		/* Register value */
  	u8 fan[3];		/* Register value */
  	u8 fan_min[3];		/* Register value */
  	s8 temp;		/* Register value */
  	s8 temp_over;		/* Register value */
  	s8 temp_hyst;		/* Register value */
  	u8 fan_div[3];		/* Register encoding, shifted right */
  	u8 vid;			/* Register encoding, combined */
  	u16 alarms;		/* Register encoding, combined */
  };
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
136
137
  static int lm78_read_value(struct lm78_data *data, u8 reg);
  static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  static struct lm78_data *lm78_update_device(struct device *dev);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
139
  static void lm78_init_device(struct lm78_data *data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  /* 7 Voltages */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
142
143
  static ssize_t show_in(struct device *dev, struct device_attribute *da,
  		       char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
145
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
147
148
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
150
151
  static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
153
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
155
156
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in_min[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
158
159
  static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
161
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
163
164
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in_max[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
166
167
  static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
  			  const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
169
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
170
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	unsigned long val = simple_strtoul(buf, NULL, 10);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
172
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
174
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	data->in_min[nr] = IN_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
176
  	lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
177
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
180
181
  static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
  			  const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
183
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
184
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	unsigned long val = simple_strtoul(buf, NULL, 10);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
186
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
188
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  	data->in_max[nr] = IN_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
190
  	lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
191
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
  	return count;
  }
  	
  #define show_in_offset(offset)					\
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
196
197
198
199
200
201
  static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO,		\
  		show_in, NULL, offset);				\
  static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR,	\
  		show_in_min, set_in_min, offset);		\
  static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR,	\
  		show_in_max, set_in_max, offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
210
211
  
  show_in_offset(0);
  show_in_offset(1);
  show_in_offset(2);
  show_in_offset(3);
  show_in_offset(4);
  show_in_offset(5);
  show_in_offset(6);
  
  /* Temperature */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
212
213
  static ssize_t show_temp(struct device *dev, struct device_attribute *da,
  			 char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	return sprintf(buf, "%d
  ", TEMP_FROM_REG(data->temp));
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
219
220
  static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	return sprintf(buf, "%d
  ", TEMP_FROM_REG(data->temp_over));
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
226
227
  static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
  			     const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
229
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	long val = simple_strtol(buf, NULL, 10);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
231
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	data->temp_over = TEMP_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
233
  	lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
234
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
237
238
  static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	return sprintf(buf, "%d
  ", TEMP_FROM_REG(data->temp_hyst));
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
244
245
  static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
  			     const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
247
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  	long val = simple_strtol(buf, NULL, 10);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
249
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	data->temp_hyst = TEMP_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
251
  	lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
252
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
259
260
261
262
  	return count;
  }
  
  static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
  static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
  		show_temp_over, set_temp_over);
  static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
  		show_temp_hyst, set_temp_hyst);
  
  /* 3 Fans */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
263
264
  static ssize_t show_fan(struct device *dev, struct device_attribute *da,
  			char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
266
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
268
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
  	return sprintf(buf, "%d
  ", FAN_FROM_REG(data->fan[nr],
  		DIV_FROM_REG(data->fan_div[nr])) );
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
273
274
  static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
  			    char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
276
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
278
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
282
  	return sprintf(buf,"%d
  ", FAN_FROM_REG(data->fan_min[nr],
  		DIV_FROM_REG(data->fan_div[nr])) );
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
283
284
  static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
  			   const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
286
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
287
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
288
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  	unsigned long val = simple_strtoul(buf, NULL, 10);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
290
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
292
  	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
293
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
296
297
  static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
  			    char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
299
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
301
302
  	return sprintf(buf, "%d
  ", DIV_FROM_REG(data->fan_div[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
  }
  
  /* Note: we save and restore the fan minimum here, because its value is
     determined in part by the fan divisor.  This follows the principle of
d6e05edc5   Andreas Mohr   spelling fixes
307
     least surprise; the user doesn't expect the fan minimum to change just
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
     because the divisor changed. */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
309
310
  static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
  			   const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
312
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
313
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
314
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
  	unsigned long val = simple_strtoul(buf, NULL, 10);
  	unsigned long min;
  	u8 reg;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
318
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
327
  	min = FAN_FROM_REG(data->fan_min[nr],
  			   DIV_FROM_REG(data->fan_div[nr]));
  
  	switch (val) {
  	case 1: data->fan_div[nr] = 0; break;
  	case 2: data->fan_div[nr] = 1; break;
  	case 4: data->fan_div[nr] = 2; break;
  	case 8: data->fan_div[nr] = 3; break;
  	default:
c40769fee   Jean Delvare   hwmon/lm78: No lo...
328
  		dev_err(dev, "fan_div value %ld not "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  			"supported. Choose one of 1, 2, 4 or 8!
  ", val);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
331
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
  		return -EINVAL;
  	}
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
334
  	reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
  	switch (nr) {
  	case 0:
  		reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
  		break;
  	case 1:
  		reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
  		break;
  	}
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
343
  	lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
  
  	data->fan_min[nr] =
  		FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
347
  	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
348
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
351
  
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
352
353
354
355
356
  #define show_fan_offset(offset)				\
  static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO,		\
  		show_fan, NULL, offset - 1);			\
  static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,	\
  		show_fan_min, set_fan_min, offset - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
  
  show_fan_offset(1);
  show_fan_offset(2);
  show_fan_offset(3);
  
  /* Fan 3 divisor is locked in H/W */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
363
364
365
366
367
  static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
  		show_fan_div, set_fan_div, 0);
  static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
  		show_fan_div, set_fan_div, 1);
  static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  
  /* VID */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
370
371
  static ssize_t show_vid(struct device *dev, struct device_attribute *da,
  			char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  {
  	struct lm78_data *data = lm78_update_device(dev);
d0d3cd696   Jean Delvare   [PATCH] hwmon: Fi...
374
375
  	return sprintf(buf, "%d
  ", vid_from_reg(data->vid, 82));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
379
  }
  static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
  
  /* Alarms */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
380
381
  static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
387
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	return sprintf(buf, "%u
  ", data->alarms);
  }
  static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
428a7039c   Jean Delvare   hwmon: (lm78) Add...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
  static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
  			  char *buf)
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	int nr = to_sensor_dev_attr(da)->index;
  	return sprintf(buf, "%u
  ", (data->alarms >> nr) & 1);
  }
  static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
  static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
  static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
  static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
  static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
  static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9);
  static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10);
  static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
  static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
  static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
  static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
407
  static struct attribute *lm78_attributes[] = {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
408
409
410
  	&sensor_dev_attr_in0_input.dev_attr.attr,
  	&sensor_dev_attr_in0_min.dev_attr.attr,
  	&sensor_dev_attr_in0_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
411
  	&sensor_dev_attr_in0_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
412
413
414
  	&sensor_dev_attr_in1_input.dev_attr.attr,
  	&sensor_dev_attr_in1_min.dev_attr.attr,
  	&sensor_dev_attr_in1_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
415
  	&sensor_dev_attr_in1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
416
417
418
  	&sensor_dev_attr_in2_input.dev_attr.attr,
  	&sensor_dev_attr_in2_min.dev_attr.attr,
  	&sensor_dev_attr_in2_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
419
  	&sensor_dev_attr_in2_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
420
421
422
  	&sensor_dev_attr_in3_input.dev_attr.attr,
  	&sensor_dev_attr_in3_min.dev_attr.attr,
  	&sensor_dev_attr_in3_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
423
  	&sensor_dev_attr_in3_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
424
425
426
  	&sensor_dev_attr_in4_input.dev_attr.attr,
  	&sensor_dev_attr_in4_min.dev_attr.attr,
  	&sensor_dev_attr_in4_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
427
  	&sensor_dev_attr_in4_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
428
429
430
  	&sensor_dev_attr_in5_input.dev_attr.attr,
  	&sensor_dev_attr_in5_min.dev_attr.attr,
  	&sensor_dev_attr_in5_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
431
  	&sensor_dev_attr_in5_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
432
433
434
  	&sensor_dev_attr_in6_input.dev_attr.attr,
  	&sensor_dev_attr_in6_min.dev_attr.attr,
  	&sensor_dev_attr_in6_max.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
435
  	&sensor_dev_attr_in6_alarm.dev_attr.attr,
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
436
437
438
  	&dev_attr_temp1_input.attr,
  	&dev_attr_temp1_max.attr,
  	&dev_attr_temp1_max_hyst.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
439
  	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
440
441
442
  	&sensor_dev_attr_fan1_input.dev_attr.attr,
  	&sensor_dev_attr_fan1_min.dev_attr.attr,
  	&sensor_dev_attr_fan1_div.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
443
  	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
444
445
446
  	&sensor_dev_attr_fan2_input.dev_attr.attr,
  	&sensor_dev_attr_fan2_min.dev_attr.attr,
  	&sensor_dev_attr_fan2_div.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
447
  	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
448
449
450
  	&sensor_dev_attr_fan3_input.dev_attr.attr,
  	&sensor_dev_attr_fan3_min.dev_attr.attr,
  	&sensor_dev_attr_fan3_div.dev_attr.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
451
  	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
452
453
454
455
456
457
458
459
460
  	&dev_attr_alarms.attr,
  	&dev_attr_cpu0_vid.attr,
  
  	NULL
  };
  
  static const struct attribute_group lm78_group = {
  	.attrs = lm78_attributes,
  };
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
461
462
463
464
465
466
467
468
469
  /*
   * ISA related code
   */
  #ifdef CONFIG_ISA
  
  /* ISA device, if found */
  static struct platform_device *pdev;
  
  static unsigned short isa_address = 0x290;
c40769fee   Jean Delvare   hwmon/lm78: No lo...
470
471
472
473
474
475
  /* I2C devices get this name attribute automatically, but for ISA devices
     we must create it by ourselves. */
  static ssize_t show_name(struct device *dev, struct device_attribute
  			 *devattr, char *buf)
  {
  	struct lm78_data *data = dev_get_drvdata(dev);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
476
477
  	return sprintf(buf, "%s
  ", data->name);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
478
479
  }
  static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
480
481
482
483
  static struct lm78_data *lm78_data_if_isa(void)
  {
  	return pdev ? platform_get_drvdata(pdev) : NULL;
  }
18c73f904   Jean Delvare   hwmon: (lm78) Det...
484
485
486
  /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
  static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
487
  	struct lm78_data *isa;
18c73f904   Jean Delvare   hwmon: (lm78) Det...
488
489
490
491
  	int i;
  
  	if (!pdev)	/* No ISA chip */
  		return 0;
18c73f904   Jean Delvare   hwmon: (lm78) Det...
492
493
494
495
496
497
498
499
500
501
  	isa = platform_get_drvdata(pdev);
  
  	if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
  		return 0;	/* Address doesn't match */
  	if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
  		return 0;	/* Chip type doesn't match */
  
  	/* We compare all the limit registers, the config register and the
  	 * interrupt mask registers */
  	for (i = 0x2b; i <= 0x3d; i++) {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
502
503
  		if (lm78_read_value(isa, i) !=
  		    i2c_smbus_read_byte_data(client, i))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
504
505
506
  			return 0;
  	}
  	if (lm78_read_value(isa, LM78_REG_CONFIG) !=
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
507
  	    i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
508
509
  		return 0;
  	for (i = 0x43; i <= 0x46; i++) {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
510
511
  		if (lm78_read_value(isa, i) !=
  		    i2c_smbus_read_byte_data(client, i))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
512
513
514
515
516
  			return 0;
  	}
  
  	return 1;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
517
518
519
520
521
522
523
524
525
526
527
528
  #else /* !CONFIG_ISA */
  
  static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
  {
  	return 0;
  }
  
  static struct lm78_data *lm78_data_if_isa(void)
  {
  	return NULL;
  }
  #endif /* CONFIG_ISA */
18c73f904   Jean Delvare   hwmon: (lm78) Det...
529

310ec7921   Jean Delvare   i2c: Drop the kin...
530
  static int lm78_i2c_detect(struct i2c_client *client,
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
531
  			   struct i2c_board_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
533
  	int i;
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
534
  	struct lm78_data *isa = lm78_data_if_isa();
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
535
536
537
  	const char *client_name;
  	struct i2c_adapter *adapter = client->adapter;
  	int address = client->addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

0c6e97317   Jean Delvare   hwmon: (lm78) Con...
539
540
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

0c6e97317   Jean Delvare   hwmon: (lm78) Con...
542
543
544
545
546
  	/* We block updates of the ISA device to minimize the risk of
  	   concurrent access to the same LM78 chip through different
  	   interfaces. */
  	if (isa)
  		mutex_lock(&isa->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

52df6440a   Jean Delvare   hwmon: Clean up d...
548
549
550
551
552
553
554
555
  	if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
  	 || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address)
  		goto err_nodev;
  
  	/* Explicitly prevent the misdetection of Winbond chips */
  	i = i2c_smbus_read_byte_data(client, 0x4f);
  	if (i == 0xa3 || i == 0x5c)
  		goto err_nodev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
  
  	/* Determine the chip type. */
52df6440a   Jean Delvare   hwmon: Clean up d...
558
559
560
561
562
563
564
565
566
567
568
569
570
571
  	i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
  	if (i == 0x00 || i == 0x20	/* LM78 */
  	 || i == 0x40)			/* LM78-J */
  		client_name = "lm78";
  	else if ((i & 0xfe) == 0xc0)
  		client_name = "lm79";
  	else
  		goto err_nodev;
  
  	if (lm78_alias_detect(client, i)) {
  		dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
  			"be the same as ISA device
  ", address);
  		goto err_nodev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  	}
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
573
574
  	if (isa)
  		mutex_unlock(&isa->update_lock);
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
575
  	strlcpy(info->type, client_name, I2C_NAME_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576

0c6e97317   Jean Delvare   hwmon: (lm78) Con...
577
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578

0c6e97317   Jean Delvare   hwmon: (lm78) Con...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
   err_nodev:
  	if (isa)
  		mutex_unlock(&isa->update_lock);
  	return -ENODEV;
  }
  
  static int lm78_i2c_probe(struct i2c_client *client,
  			  const struct i2c_device_id *id)
  {
  	struct lm78_data *data;
  	int err;
  
  	data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
  
  	i2c_set_clientdata(client, data);
  	data->client = client;
  	data->type = id->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
  
  	/* Initialize the LM78 chip */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
600
  	lm78_init_device(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  	/* Register sysfs hooks */
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
603
604
  	err = sysfs_create_group(&client->dev.kobj, &lm78_group);
  	if (err)
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
605
  		goto ERROR3;
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
606
  	data->hwmon_dev = hwmon_device_register(&client->dev);
1beeffe43   Tony Jones   hwmon: Convert fr...
607
608
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
609
  		goto ERROR4;
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
610
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  	return 0;
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
612
  ERROR4:
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
613
  	sysfs_remove_group(&client->dev.kobj, &lm78_group);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
614
  ERROR3:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  	kfree(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
  	return err;
  }
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
618
  static int lm78_i2c_remove(struct i2c_client *client)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
  {
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
620
  	struct lm78_data *data = i2c_get_clientdata(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621

1beeffe43   Tony Jones   hwmon: Convert fr...
622
  	hwmon_device_unregister(data->hwmon_dev);
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
623
  	sysfs_remove_group(&client->dev.kobj, &lm78_group);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
624
625
626
627
  	kfree(data);
  
  	return 0;
  }
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
628
629
630
631
632
633
  static const struct i2c_device_id lm78_i2c_id[] = {
  	{ "lm78", lm78 },
  	{ "lm79", lm79 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
634

ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
635
636
637
638
639
640
641
642
643
644
645
  static struct i2c_driver lm78_driver = {
  	.class		= I2C_CLASS_HWMON,
  	.driver = {
  		.name	= "lm78",
  	},
  	.probe		= lm78_i2c_probe,
  	.remove		= lm78_i2c_remove,
  	.id_table	= lm78_i2c_id,
  	.detect		= lm78_i2c_detect,
  	.address_list	= normal_i2c,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646

44bbe87e9   Steven Cole   [PATCH] Spelling ...
647
  /* The SMBus locks itself, but ISA access must be locked explicitly! 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
     We don't want to lock the whole ISA bus, so we lock each client
     separately.
     We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
     would slow down the LM78 access and should not be necessary.  */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
652
  static int lm78_read_value(struct lm78_data *data, u8 reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
654
  	struct i2c_client *client = data->client;
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
655

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
656
  #ifdef CONFIG_ISA
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
657
  	if (!client) { /* ISA device */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
658
  		int res;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
659
  		mutex_lock(&data->lock);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
660
661
  		outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
  		res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
662
  		mutex_unlock(&data->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
  		return res;
  	} else
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
665
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
  		return i2c_smbus_read_byte_data(client, reg);
  }
44bbe87e9   Steven Cole   [PATCH] Spelling ...
668
  /* The SMBus locks itself, but ISA access muse be locked explicitly! 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
672
673
674
     We don't want to lock the whole ISA bus, so we lock each client
     separately.
     We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
     would slow down the LM78 access and should not be necessary. 
     There are some ugly typecasts here, but the good new is - they should
     nowhere else be necessary! */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
675
  static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
677
  	struct i2c_client *client = data->client;
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
678

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
679
  #ifdef CONFIG_ISA
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
680
  	if (!client) { /* ISA device */
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
681
  		mutex_lock(&data->lock);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
682
683
  		outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
  		outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
684
  		mutex_unlock(&data->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
  		return 0;
  	} else
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
687
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
  		return i2c_smbus_write_byte_data(client, reg, value);
  }
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
690
  static void lm78_init_device(struct lm78_data *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
692
693
  	u8 config;
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
  
  	/* Start monitoring */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
696
  	config = lm78_read_value(data, LM78_REG_CONFIG);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
697
  	if ((config & 0x09) != 0x01)
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
698
  		lm78_write_value(data, LM78_REG_CONFIG,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
  				 (config & 0xf7) | 0x01);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
700
701
702
  
  	/* A few vars need to be filled upon startup */
  	for (i = 0; i < 3; i++) {
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
703
  		data->fan_min[i] = lm78_read_value(data,
c40769fee   Jean Delvare   hwmon/lm78: No lo...
704
705
706
707
  					LM78_REG_FAN_MIN(i));
  	}
  
  	mutex_init(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
710
711
  }
  
  static struct lm78_data *lm78_update_device(struct device *dev)
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
712
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  	int i;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
714
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  	    || !data->valid) {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
718
719
  		dev_dbg(dev, "Starting lm78 update
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
  
  		for (i = 0; i <= 6; i++) {
  			data->in[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
723
  			    lm78_read_value(data, LM78_REG_IN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  			data->in_min[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
725
  			    lm78_read_value(data, LM78_REG_IN_MIN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  			data->in_max[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
727
  			    lm78_read_value(data, LM78_REG_IN_MAX(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
  		}
  		for (i = 0; i < 3; i++) {
  			data->fan[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
731
  			    lm78_read_value(data, LM78_REG_FAN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
  			data->fan_min[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
733
  			    lm78_read_value(data, LM78_REG_FAN_MIN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  		}
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
735
  		data->temp = lm78_read_value(data, LM78_REG_TEMP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  		data->temp_over =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
737
  		    lm78_read_value(data, LM78_REG_TEMP_OVER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  		data->temp_hyst =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
739
740
  		    lm78_read_value(data, LM78_REG_TEMP_HYST);
  		i = lm78_read_value(data, LM78_REG_VID_FANDIV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
  		data->vid = i & 0x0f;
  		if (data->type == lm79)
  			data->vid |=
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
744
  			    (lm78_read_value(data, LM78_REG_CHIPID) &
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
749
  			     0x01) << 4;
  		else
  			data->vid |= 0x10;
  		data->fan_div[0] = (i >> 4) & 0x03;
  		data->fan_div[1] = i >> 6;
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
750
751
  		data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
  		    (lm78_read_value(data, LM78_REG_ALARM2) << 8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
  		data->last_updated = jiffies;
  		data->valid = 1;
  
  		data->fan_div[2] = 1;
  	}
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
757
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
760
  
  	return data;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
761
  #ifdef CONFIG_ISA
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
  static int __devinit lm78_isa_probe(struct platform_device *pdev)
  {
  	int err;
  	struct lm78_data *data;
  	struct resource *res;
  
  	/* Reserve the ISA region */
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  	if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
  		err = -EBUSY;
  		goto exit;
  	}
  
  	data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
  	if (!data) {
  		err = -ENOMEM;
  		goto exit_release_region;
  	}
  	mutex_init(&data->lock);
  	data->isa_addr = res->start;
  	platform_set_drvdata(pdev, data);
  
  	if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
  		data->type = lm79;
  		data->name = "lm79";
  	} else {
  		data->type = lm78;
  		data->name = "lm78";
  	}
  
  	/* Initialize the LM78 chip */
  	lm78_init_device(data);
  
  	/* Register sysfs hooks */
  	if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
  	 || (err = device_create_file(&pdev->dev, &dev_attr_name)))
  		goto exit_remove_files;
  
  	data->hwmon_dev = hwmon_device_register(&pdev->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
  		goto exit_remove_files;
  	}
  
  	return 0;
  
   exit_remove_files:
  	sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
  	device_remove_file(&pdev->dev, &dev_attr_name);
  	kfree(data);
   exit_release_region:
  	release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
   exit:
  	return err;
  }
  
  static int __devexit lm78_isa_remove(struct platform_device *pdev)
  {
  	struct lm78_data *data = platform_get_drvdata(pdev);
  	struct resource *res;
  
  	hwmon_device_unregister(data->hwmon_dev);
  	sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
  	device_remove_file(&pdev->dev, &dev_attr_name);
  	kfree(data);
  
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  	release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
  
  	return 0;
  }
  
  static struct platform_driver lm78_isa_driver = {
  	.driver = {
  		.owner	= THIS_MODULE,
  		.name	= "lm78",
  	},
  	.probe		= lm78_isa_probe,
  	.remove		= __devexit_p(lm78_isa_remove),
  };
c40769fee   Jean Delvare   hwmon/lm78: No lo...
842
843
844
845
  /* return 1 if a supported chip is found, 0 otherwise */
  static int __init lm78_isa_found(unsigned short address)
  {
  	int val, save, found = 0;
197027e6e   Jean Delvare   hwmon: (lm78) Req...
846
847
848
849
850
851
852
  	int port;
  
  	/* Some boards declare base+0 to base+7 as a PNP device, some base+4
  	 * to base+7 and some base+5 to base+6. So we better request each port
  	 * individually for the probing phase. */
  	for (port = address; port < address + LM78_EXTENT; port++) {
  		if (!request_region(port, 1, "lm78")) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
853
854
  			pr_debug("Failed to request port 0x%x
  ", port);
197027e6e   Jean Delvare   hwmon: (lm78) Req...
855
856
  			goto release;
  		}
47c15532d   Jean Delvare   hwmon: (lm78) Fix...
857
  	}
c40769fee   Jean Delvare   hwmon/lm78: No lo...
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
  
  #define REALLY_SLOW_IO
  	/* We need the timeouts for at least some LM78-like
  	   chips. But only if we read 'undefined' registers. */
  	val = inb_p(address + 1);
  	if (inb_p(address + 2) != val
  	 || inb_p(address + 3) != val
  	 || inb_p(address + 7) != val)
  		goto release;
  #undef REALLY_SLOW_IO
  
  	/* We should be able to change the 7 LSB of the address port. The
  	   MSB (busy flag) should be clear initially, set after the write. */
  	save = inb_p(address + LM78_ADDR_REG_OFFSET);
  	if (save & 0x80)
  		goto release;
  	val = ~save & 0x7f;
  	outb_p(val, address + LM78_ADDR_REG_OFFSET);
  	if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
  		outb_p(save, address + LM78_ADDR_REG_OFFSET);
  		goto release;
  	}
  
  	/* We found a device, now see if it could be an LM78 */
  	outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
  	val = inb_p(address + LM78_DATA_REG_OFFSET);
  	if (val & 0x80)
  		goto release;
  	outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
  	val = inb_p(address + LM78_DATA_REG_OFFSET);
  	if (val < 0x03 || val > 0x77)	/* Not a valid I2C address */
  		goto release;
  
  	/* The busy flag should be clear again */
  	if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
  		goto release;
  
  	/* Explicitly prevent the misdetection of Winbond chips */
  	outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
  	val = inb_p(address + LM78_DATA_REG_OFFSET);
  	if (val == 0xa3 || val == 0x5c)
  		goto release;
  
  	/* Explicitly prevent the misdetection of ITE chips */
  	outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
  	val = inb_p(address + LM78_DATA_REG_OFFSET);
  	if (val == 0x90)
  		goto release;
  
  	/* Determine the chip type */
  	outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
  	val = inb_p(address + LM78_DATA_REG_OFFSET);
acf346a31   Hans de Goede   hwmon: fix lm78 d...
910
  	if (val == 0x00 || val == 0x20	/* LM78 */
c40769fee   Jean Delvare   hwmon/lm78: No lo...
911
912
913
914
915
  	 || val == 0x40			/* LM78-J */
  	 || (val & 0xfe) == 0xc0)	/* LM79 */
  		found = 1;
  
  	if (found)
ce47da742   Joe Perches   hwmon: (lm78) Use...
916
917
  		pr_info("Found an %s chip at %#x
  ",
c40769fee   Jean Delvare   hwmon/lm78: No lo...
918
919
920
  			val & 0x80 ? "LM79" : "LM78", (int)address);
  
   release:
197027e6e   Jean Delvare   hwmon: (lm78) Req...
921
922
  	for (port--; port >= address; port--)
  		release_region(port, 1);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
923
924
925
926
927
928
929
  	return found;
  }
  
  static int __init lm78_isa_device_add(unsigned short address)
  {
  	struct resource res = {
  		.start	= address,
15bde2f1a   Jean Delvare   hwmon: End of I/O...
930
  		.end	= address + LM78_EXTENT - 1,
c40769fee   Jean Delvare   hwmon/lm78: No lo...
931
932
933
934
935
936
937
938
  		.name	= "lm78",
  		.flags	= IORESOURCE_IO,
  	};
  	int err;
  
  	pdev = platform_device_alloc("lm78", address);
  	if (!pdev) {
  		err = -ENOMEM;
ce47da742   Joe Perches   hwmon: (lm78) Use...
939
940
  		pr_err("Device allocation failed
  ");
c40769fee   Jean Delvare   hwmon/lm78: No lo...
941
942
943
944
945
  		goto exit;
  	}
  
  	err = platform_device_add_resources(pdev, &res, 1);
  	if (err) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
946
947
  		pr_err("Device resource addition failed (%d)
  ", err);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
948
949
950
951
952
  		goto exit_device_put;
  	}
  
  	err = platform_device_add(pdev);
  	if (err) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
953
954
  		pr_err("Device addition failed (%d)
  ", err);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
955
956
957
958
959
960
961
962
963
964
965
  		goto exit_device_put;
  	}
  
  	return 0;
  
   exit_device_put:
  	platform_device_put(pdev);
   exit:
  	pdev = NULL;
  	return err;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
966
  static int __init lm78_isa_register(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
  {
fde095090   Jean Delvare   [PATCH] I2C: Sepa...
968
  	int res;
c40769fee   Jean Delvare   hwmon/lm78: No lo...
969
970
971
  	if (lm78_isa_found(isa_address)) {
  		res = platform_driver_register(&lm78_isa_driver);
  		if (res)
18c73f904   Jean Delvare   hwmon: (lm78) Det...
972
  			goto exit;
fde095090   Jean Delvare   [PATCH] I2C: Sepa...
973

c40769fee   Jean Delvare   hwmon/lm78: No lo...
974
975
976
977
978
  		/* Sets global pdev as a side effect */
  		res = lm78_isa_device_add(isa_address);
  		if (res)
  			goto exit_unreg_isa_driver;
  	}
fde095090   Jean Delvare   [PATCH] I2C: Sepa...
979
980
  
  	return 0;
c40769fee   Jean Delvare   hwmon/lm78: No lo...
981
982
983
  
   exit_unreg_isa_driver:
  	platform_driver_unregister(&lm78_isa_driver);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
984
985
   exit:
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
987
  static void lm78_isa_unregister(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
989
990
991
992
  	if (pdev) {
  		platform_device_unregister(pdev);
  		platform_driver_unregister(&lm78_isa_driver);
  	}
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
  }
  #else /* !CONFIG_ISA */
  
  static int __init lm78_isa_register(void)
  {
  	return 0;
  }
  
  static void lm78_isa_unregister(void)
  {
  }
  #endif /* CONFIG_ISA */
  
  static int __init sm_lm78_init(void)
  {
  	int res;
  
  	/* We register the ISA device first, so that we can skip the
  	 * registration of an I2C interface to the same device. */
  	res = lm78_isa_register();
  	if (res)
  		goto exit;
  
  	res = i2c_add_driver(&lm78_driver);
  	if (res)
  		goto exit_unreg_isa_device;
  
  	return 0;
  
   exit_unreg_isa_device:
  	lm78_isa_unregister();
   exit:
  	return res;
  }
  
  static void __exit sm_lm78_exit(void)
  {
  	lm78_isa_unregister();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
  	i2c_del_driver(&lm78_driver);
  }
156e2d1ad   Jean Delvare   hwmon: (lm78) Bec...
1033
  MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <khali@linux-fr.org>");
27fe048eb   Jean Delvare   [PATCH] hwmon: ki...
1034
  MODULE_DESCRIPTION("LM78/LM79 driver");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
1038
  MODULE_LICENSE("GPL");
  
  module_init(sm_lm78_init);
  module_exit(sm_lm78_exit);