Blame view

drivers/hwmon/lm78.c 27.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
2
3
4
   * lm78.c - Part of lm_sensors, Linux kernel modules for hardware
   *	    monitoring
   * Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
7c81c60f3   Jean Delvare   Update Jean Delva...
5
   * Copyright (c) 2007, 2011  Jean Delvare <jdelvare@suse.de>
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21

ce47da742   Joe Perches   hwmon: (lm78) Use...
22
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  #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...
28
  #include <linux/hwmon.h>
19f673edd   Jean Delvare   [PATCH] hwmon: hw...
29
  #include <linux/hwmon-vid.h>
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
30
  #include <linux/hwmon-sysfs.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
31
  #include <linux/err.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
32
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  /* Addresses to scan */
25e9c86d5   Mark M. Hoffman   hwmon: normal_i2c...
41
42
  static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
  						0x2e, 0x2f, I2C_CLIENT_END };
e5e9f44c2   Jean Delvare   i2c: Drop I2C_CLI...
43
  enum chips { lm78, lm79 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  
  /* 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
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
74
75
76
77
  /*
   * Conversions. Rounding and limit checking is only done on the TO_REG
   * variants.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78

9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
79
80
81
82
  /*
   * IN: mV (0V to 4.08V)
   * REG: 16mV/bit
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
  static inline u8 IN_TO_REG(unsigned long val)
  {
2a844c148   Guenter Roeck   hwmon: Replace SE...
85
  	unsigned long nval = clamp_val(val, 0, 4080);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
91
92
93
  	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;
3806b45ba   Dan Carpenter   hwmon: Prevent so...
94
95
  	if (rpm > 1350000)
  		return 1;
2a844c148   Guenter Roeck   hwmon: Replace SE...
96
  	return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
  }
  
  static inline int FAN_FROM_REG(u8 val, int div)
  {
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
101
  	return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  }
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
103
104
105
106
  /*
   * TEMP: mC (-128C to +127C)
   * REG: 1C/bit, two's complement
   */
1074d683a   Guenter Roeck   hwmon: (lm78) Fix...
107
  static inline s8 TEMP_TO_REG(long val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  {
2a844c148   Guenter Roeck   hwmon: Replace SE...
109
  	int nval = clamp_val(val, -128000, 127000) ;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
110
  	return nval < 0 ? (nval - 500) / 1000 : (nval + 500) / 1000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
115
116
  }
  
  static inline int TEMP_FROM_REG(s8 val)
  {
  	return val * 1000;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
  #define DIV_FROM_REG(val) (1 << (val))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  struct lm78_data {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
119
  	struct i2c_client *client;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
120
  	struct mutex lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  	enum chips type;
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
122
123
124
  	/* For ISA device only */
  	const char *name;
  	int isa_addr;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
125
  	struct mutex update_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  	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...
141
142
  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
143
  static struct lm78_data *lm78_update_device(struct device *dev);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
144
  static void lm78_init_device(struct lm78_data *data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  /* 7 Voltages */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
147
148
  static ssize_t show_in(struct device *dev, struct device_attribute *da,
  		       char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
150
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
152
153
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
155
156
  static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
158
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
160
161
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in_min[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
163
164
  static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
166
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
168
169
  	return sprintf(buf, "%d
  ", IN_FROM_REG(data->in_max[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
171
172
  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
173
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
174
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
175
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
176
  	int nr = attr->index;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
177
178
179
180
181
182
  	unsigned long val;
  	int err;
  
  	err = kstrtoul(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
184
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	data->in_min[nr] = IN_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
186
  	lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
187
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
190
191
  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
192
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
193
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
194
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
195
  	int nr = attr->index;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
196
197
198
199
200
201
  	unsigned long val;
  	int err;
  
  	err = kstrtoul(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
203
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
  	data->in_max[nr] = IN_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
205
  	lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
206
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
  	return count;
  }
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
209

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  #define show_in_offset(offset)					\
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
211
212
213
214
215
216
  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
217
218
219
220
221
222
223
224
225
226
  
  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...
227
228
  static ssize_t show_temp(struct device *dev, struct device_attribute *da,
  			 char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
  {
  	struct lm78_data *data = lm78_update_device(dev);
  	return sprintf(buf, "%d
  ", TEMP_FROM_REG(data->temp));
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
234
235
  static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
  {
  	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...
241
242
  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
243
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
244
  	struct lm78_data *data = dev_get_drvdata(dev);
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
245
246
247
248
249
250
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
252
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  	data->temp_over = TEMP_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
254
  	lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
255
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
258
259
  static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
  {
  	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...
265
266
  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
267
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
268
  	struct lm78_data *data = dev_get_drvdata(dev);
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
269
270
271
272
273
274
  	long val;
  	int err;
  
  	err = kstrtol(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
276
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  	data->temp_hyst = TEMP_TO_REG(val);
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
278
  	lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
279
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
288
289
  	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...
290
291
  static ssize_t show_fan(struct device *dev, struct device_attribute *da,
  			char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
293
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
295
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
  	return sprintf(buf, "%d
  ", FAN_FROM_REG(data->fan[nr],
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
298
  		DIV_FROM_REG(data->fan_div[nr])));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
300
301
  static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
  			    char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
303
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
305
  	int nr = attr->index;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
306
307
308
  	return sprintf(buf, "%d
  ", FAN_FROM_REG(data->fan_min[nr],
  		DIV_FROM_REG(data->fan_div[nr])));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
310
311
  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
312
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
313
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
314
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
315
  	int nr = attr->index;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
316
317
318
319
320
321
  	unsigned long val;
  	int err;
  
  	err = kstrtoul(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
323
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
  	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
325
  	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
326
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
329
330
  static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
  			    char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
332
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  	struct lm78_data *data = lm78_update_device(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
334
335
  	return sprintf(buf, "%d
  ", DIV_FROM_REG(data->fan_div[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  }
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
337
338
339
340
341
342
  /*
   * 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
   * least surprise; the user doesn't expect the fan minimum to change just
   * because the divisor changed.
   */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
343
344
  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
345
  {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
346
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
347
  	struct lm78_data *data = dev_get_drvdata(dev);
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
348
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
  	unsigned long min;
  	u8 reg;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
351
352
353
354
355
356
  	unsigned long val;
  	int err;
  
  	err = kstrtoul(buf, 10, &val);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
358
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
  	min = FAN_FROM_REG(data->fan_min[nr],
  			   DIV_FROM_REG(data->fan_div[nr]));
  
  	switch (val) {
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
363
364
365
366
367
368
369
370
371
372
373
374
  	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
375
  	default:
b55f37572   Guenter Roeck   hwmon: Fix checkp...
376
377
378
379
  		dev_err(dev,
  			"fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!
  ",
  			val);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
380
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
  		return -EINVAL;
  	}
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
383
  	reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
391
  	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...
392
  	lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
  
  	data->fan_min[nr] =
  		FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
396
  	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
397
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
  
  	return count;
  }
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
401
402
403
404
405
  #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
406
407
408
409
410
411
  
  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...
412
413
414
415
416
  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
417
418
  
  /* VID */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
419
420
  static ssize_t show_vid(struct device *dev, struct device_attribute *da,
  			char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
  {
  	struct lm78_data *data = lm78_update_device(dev);
d0d3cd696   Jean Delvare   [PATCH] hwmon: Fi...
423
424
  	return sprintf(buf, "%d
  ", vid_from_reg(data->vid, 82));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
  }
  static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
  
  /* Alarms */
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
429
430
  static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
  {
  	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...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  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);
8eb406100   Axel Lin   hwmon: (lm78) Con...
456
  static struct attribute *lm78_attrs[] = {
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
457
458
459
  	&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...
460
  	&sensor_dev_attr_in0_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
461
462
463
  	&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...
464
  	&sensor_dev_attr_in1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
465
466
467
  	&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...
468
  	&sensor_dev_attr_in2_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
469
470
471
  	&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...
472
  	&sensor_dev_attr_in3_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
473
474
475
  	&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...
476
  	&sensor_dev_attr_in4_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
477
478
479
  	&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...
480
  	&sensor_dev_attr_in5_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
481
482
483
  	&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...
484
  	&sensor_dev_attr_in6_alarm.dev_attr.attr,
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
485
486
487
  	&dev_attr_temp1_input.attr,
  	&dev_attr_temp1_max.attr,
  	&dev_attr_temp1_max_hyst.attr,
428a7039c   Jean Delvare   hwmon: (lm78) Add...
488
  	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
489
490
491
  	&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...
492
  	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
493
494
495
  	&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...
496
  	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
247dde4cd   Jean Delvare   hwmon/lm78: Use d...
497
498
499
  	&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...
500
  	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
501
502
503
504
505
  	&dev_attr_alarms.attr,
  	&dev_attr_cpu0_vid.attr,
  
  	NULL
  };
8eb406100   Axel Lin   hwmon: (lm78) Con...
506
  ATTRIBUTE_GROUPS(lm78);
c1685f61b   Mark M. Hoffman   hwmon: Fix unchec...
507

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
508
509
510
511
512
513
514
515
516
  /*
   * ISA related code
   */
  #ifdef CONFIG_ISA
  
  /* ISA device, if found */
  static struct platform_device *pdev;
  
  static unsigned short isa_address = 0x290;
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
517
518
519
520
  static struct lm78_data *lm78_data_if_isa(void)
  {
  	return pdev ? platform_get_drvdata(pdev) : NULL;
  }
18c73f904   Jean Delvare   hwmon: (lm78) Det...
521
522
523
  /* 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...
524
  	struct lm78_data *isa;
18c73f904   Jean Delvare   hwmon: (lm78) Det...
525
526
527
528
  	int i;
  
  	if (!pdev)	/* No ISA chip */
  		return 0;
18c73f904   Jean Delvare   hwmon: (lm78) Det...
529
530
531
532
533
534
  	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 */
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
535
536
537
538
  	/*
  	 * We compare all the limit registers, the config register and the
  	 * interrupt mask registers
  	 */
18c73f904   Jean Delvare   hwmon: (lm78) Det...
539
  	for (i = 0x2b; i <= 0x3d; i++) {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
540
541
  		if (lm78_read_value(isa, i) !=
  		    i2c_smbus_read_byte_data(client, i))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
542
543
544
  			return 0;
  	}
  	if (lm78_read_value(isa, LM78_REG_CONFIG) !=
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
545
  	    i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
546
547
  		return 0;
  	for (i = 0x43; i <= 0x46; i++) {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
548
549
  		if (lm78_read_value(isa, i) !=
  		    i2c_smbus_read_byte_data(client, i))
18c73f904   Jean Delvare   hwmon: (lm78) Det...
550
551
552
553
554
  			return 0;
  	}
  
  	return 1;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
555
556
557
558
559
560
561
562
563
564
565
566
  #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...
567

310ec7921   Jean Delvare   i2c: Drop the kin...
568
  static int lm78_i2c_detect(struct i2c_client *client,
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
569
  			   struct i2c_board_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
571
  	int i;
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
572
  	struct lm78_data *isa = lm78_data_if_isa();
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
573
574
575
  	const char *client_name;
  	struct i2c_adapter *adapter = client->adapter;
  	int address = client->addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576

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

9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
580
581
582
583
584
  	/*
  	 * We block updates of the ISA device to minimize the risk of
  	 * concurrent access to the same LM78 chip through different
  	 * interfaces.
  	 */
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
585
586
  	if (isa)
  		mutex_lock(&isa->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587

52df6440a   Jean Delvare   hwmon: Clean up d...
588
589
590
591
592
593
594
595
  	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
596
597
  
  	/* Determine the chip type. */
52df6440a   Jean Delvare   hwmon: Clean up d...
598
599
600
601
602
603
604
605
606
607
  	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)) {
b55f37572   Guenter Roeck   hwmon: Fix checkp...
608
609
610
611
  		dev_dbg(&adapter->dev,
  			"Device at 0x%02x appears to be the same as ISA device
  ",
  			address);
52df6440a   Jean Delvare   hwmon: Clean up d...
612
  		goto err_nodev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  	}
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
614
615
  	if (isa)
  		mutex_unlock(&isa->update_lock);
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
616
  	strlcpy(info->type, client_name, I2C_NAME_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617

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

0c6e97317   Jean Delvare   hwmon: (lm78) Con...
620
621
622
623
624
625
626
627
628
   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)
  {
8eb406100   Axel Lin   hwmon: (lm78) Con...
629
630
  	struct device *dev = &client->dev;
  	struct device *hwmon_dev;
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
631
  	struct lm78_data *data;
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
632

8eb406100   Axel Lin   hwmon: (lm78) Con...
633
  	data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
634
635
  	if (!data)
  		return -ENOMEM;
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
636
637
  	data->client = client;
  	data->type = id->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
  
  	/* Initialize the LM78 chip */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
640
  	lm78_init_device(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641

8eb406100   Axel Lin   hwmon: (lm78) Con...
642
643
644
  	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
  							   data, lm78_groups);
  	return PTR_ERR_OR_ZERO(hwmon_dev);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
645
  }
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
646
647
648
649
650
651
  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...
652

ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
653
654
655
656
657
658
  static struct i2c_driver lm78_driver = {
  	.class		= I2C_CLASS_HWMON,
  	.driver = {
  		.name	= "lm78",
  	},
  	.probe		= lm78_i2c_probe,
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
659
660
661
662
  	.id_table	= lm78_i2c_id,
  	.detect		= lm78_i2c_detect,
  	.address_list	= normal_i2c,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663

9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
664
665
666
667
668
669
670
  /*
   * The SMBus locks itself, but ISA access must be locked explicitly!
   * 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...
671
  static int lm78_read_value(struct lm78_data *data, u8 reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
673
  	struct i2c_client *client = data->client;
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
674

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
675
  #ifdef CONFIG_ISA
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
676
  	if (!client) { /* ISA device */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
677
  		int res;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
678
  		mutex_lock(&data->lock);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
679
680
  		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...
681
  		mutex_unlock(&data->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
  		return res;
  	} else
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
684
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
  		return i2c_smbus_read_byte_data(client, reg);
  }
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
687
  static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  {
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
689
  	struct i2c_client *client = data->client;
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
690

90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
691
  #ifdef CONFIG_ISA
0c6e97317   Jean Delvare   hwmon: (lm78) Con...
692
  	if (!client) { /* ISA device */
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
693
  		mutex_lock(&data->lock);
6e1b5029d   Jean Delvare   hwmon: (lm78) Sto...
694
695
  		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...
696
  		mutex_unlock(&data->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
  		return 0;
  	} else
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
699
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
  		return i2c_smbus_write_byte_data(client, reg, value);
  }
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
702
  static void lm78_init_device(struct lm78_data *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
704
705
  	u8 config;
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
  
  	/* Start monitoring */
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
708
  	config = lm78_read_value(data, LM78_REG_CONFIG);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
709
  	if ((config & 0x09) != 0x01)
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
710
  		lm78_write_value(data, LM78_REG_CONFIG,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  				 (config & 0xf7) | 0x01);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
712
713
714
  
  	/* A few vars need to be filled upon startup */
  	for (i = 0; i < 3; i++) {
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
715
  		data->fan_min[i] = lm78_read_value(data,
c40769fee   Jean Delvare   hwmon/lm78: No lo...
716
717
718
719
  					LM78_REG_FAN_MIN(i));
  	}
  
  	mutex_init(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
  }
  
  static struct lm78_data *lm78_update_device(struct device *dev)
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
724
  	struct lm78_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
  	int i;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
726
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
729
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  	    || !data->valid) {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
730
731
  		dev_dbg(dev, "Starting lm78 update
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
  
  		for (i = 0; i <= 6; i++) {
  			data->in[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
735
  			    lm78_read_value(data, LM78_REG_IN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  			data->in_min[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
737
  			    lm78_read_value(data, LM78_REG_IN_MIN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  			data->in_max[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
739
  			    lm78_read_value(data, LM78_REG_IN_MAX(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
  		}
  		for (i = 0; i < 3; i++) {
  			data->fan[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
743
  			    lm78_read_value(data, LM78_REG_FAN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
  			data->fan_min[i] =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
745
  			    lm78_read_value(data, LM78_REG_FAN_MIN(i));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
  		}
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
747
  		data->temp = lm78_read_value(data, LM78_REG_TEMP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
  		data->temp_over =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
749
  		    lm78_read_value(data, LM78_REG_TEMP_OVER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
  		data->temp_hyst =
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
751
752
  		    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
753
754
755
  		data->vid = i & 0x0f;
  		if (data->type == lm79)
  			data->vid |=
c59cc301e   Jean Delvare   hwmon/lm78: Be le...
756
  			    (lm78_read_value(data, LM78_REG_CHIPID) &
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
759
760
761
  			     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...
762
763
  		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
764
765
766
767
768
  		data->last_updated = jiffies;
  		data->valid = 1;
  
  		data->fan_div[2] = 1;
  	}
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
769
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
772
  
  	return data;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
773
  #ifdef CONFIG_ISA
6c931ae1c   Bill Pemberton   hwmon: remove use...
774
  static int lm78_isa_probe(struct platform_device *pdev)
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
775
  {
8eb406100   Axel Lin   hwmon: (lm78) Con...
776
777
  	struct device *dev = &pdev->dev;
  	struct device *hwmon_dev;
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
778
779
780
781
782
  	struct lm78_data *data;
  	struct resource *res;
  
  	/* Reserve the ISA region */
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
8eb406100   Axel Lin   hwmon: (lm78) Con...
783
  	if (!devm_request_region(dev, res->start + LM78_ADDR_REG_OFFSET,
7b7bb90ca   Guenter Roeck   hwmon: (lm78) Con...
784
785
  				 2, "lm78"))
  		return -EBUSY;
8eb406100   Axel Lin   hwmon: (lm78) Con...
786
  	data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
7b7bb90ca   Guenter Roeck   hwmon: (lm78) Con...
787
788
  	if (!data)
  		return -ENOMEM;
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
789

ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  	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);
8eb406100   Axel Lin   hwmon: (lm78) Con...
804
805
806
  	hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
  							   data, lm78_groups);
  	return PTR_ERR_OR_ZERO(hwmon_dev);
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
807
808
809
810
  }
  
  static struct platform_driver lm78_isa_driver = {
  	.driver = {
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
811
812
813
  		.name	= "lm78",
  	},
  	.probe		= lm78_isa_probe,
ed4cebdf9   Jean Delvare   hwmon: (lm78) Avo...
814
  };
c40769fee   Jean Delvare   hwmon/lm78: No lo...
815
816
817
818
  /* 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...
819
  	int port;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
820
821
  	/*
  	 * Some boards declare base+0 to base+7 as a PNP device, some base+4
197027e6e   Jean Delvare   hwmon: (lm78) Req...
822
  	 * to base+7 and some base+5 to base+6. So we better request each port
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
823
824
  	 * individually for the probing phase.
  	 */
197027e6e   Jean Delvare   hwmon: (lm78) Req...
825
826
  	for (port = address; port < address + LM78_EXTENT; port++) {
  		if (!request_region(port, 1, "lm78")) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
827
828
  			pr_debug("Failed to request port 0x%x
  ", port);
197027e6e   Jean Delvare   hwmon: (lm78) Req...
829
830
  			goto release;
  		}
47c15532d   Jean Delvare   hwmon: (lm78) Fix...
831
  	}
c40769fee   Jean Delvare   hwmon/lm78: No lo...
832
833
  
  #define REALLY_SLOW_IO
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
834
835
836
837
  	/*
  	 * We need the timeouts for at least some LM78-like
  	 * chips. But only if we read 'undefined' registers.
  	 */
c40769fee   Jean Delvare   hwmon/lm78: No lo...
838
839
840
841
842
843
  	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
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
844
845
846
847
  	/*
  	 * 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.
  	 */
c40769fee   Jean Delvare   hwmon/lm78: No lo...
848
849
850
851
852
853
854
855
856
857
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
  	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...
887
  	if (val == 0x00 || val == 0x20	/* LM78 */
c40769fee   Jean Delvare   hwmon/lm78: No lo...
888
889
890
891
892
  	 || val == 0x40			/* LM78-J */
  	 || (val & 0xfe) == 0xc0)	/* LM79 */
  		found = 1;
  
  	if (found)
ce47da742   Joe Perches   hwmon: (lm78) Use...
893
894
  		pr_info("Found an %s chip at %#x
  ",
c40769fee   Jean Delvare   hwmon/lm78: No lo...
895
896
897
  			val & 0x80 ? "LM79" : "LM78", (int)address);
  
   release:
197027e6e   Jean Delvare   hwmon: (lm78) Req...
898
899
  	for (port--; port >= address; port--)
  		release_region(port, 1);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
900
901
902
903
904
905
906
  	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...
907
  		.end	= address + LM78_EXTENT - 1,
c40769fee   Jean Delvare   hwmon/lm78: No lo...
908
909
910
911
912
913
914
915
  		.name	= "lm78",
  		.flags	= IORESOURCE_IO,
  	};
  	int err;
  
  	pdev = platform_device_alloc("lm78", address);
  	if (!pdev) {
  		err = -ENOMEM;
ce47da742   Joe Perches   hwmon: (lm78) Use...
916
917
  		pr_err("Device allocation failed
  ");
c40769fee   Jean Delvare   hwmon/lm78: No lo...
918
919
920
921
922
  		goto exit;
  	}
  
  	err = platform_device_add_resources(pdev, &res, 1);
  	if (err) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
923
924
  		pr_err("Device resource addition failed (%d)
  ", err);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
925
926
927
928
929
  		goto exit_device_put;
  	}
  
  	err = platform_device_add(pdev);
  	if (err) {
ce47da742   Joe Perches   hwmon: (lm78) Use...
930
931
  		pr_err("Device addition failed (%d)
  ", err);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
932
933
934
935
936
937
938
939
940
941
942
  		goto exit_device_put;
  	}
  
  	return 0;
  
   exit_device_put:
  	platform_device_put(pdev);
   exit:
  	pdev = NULL;
  	return err;
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
943
  static int __init lm78_isa_register(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
  {
fde095090   Jean Delvare   [PATCH] I2C: Sepa...
945
  	int res;
c40769fee   Jean Delvare   hwmon/lm78: No lo...
946
947
948
  	if (lm78_isa_found(isa_address)) {
  		res = platform_driver_register(&lm78_isa_driver);
  		if (res)
18c73f904   Jean Delvare   hwmon: (lm78) Det...
949
  			goto exit;
fde095090   Jean Delvare   [PATCH] I2C: Sepa...
950

c40769fee   Jean Delvare   hwmon/lm78: No lo...
951
952
953
954
955
  		/* 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...
956
957
  
  	return 0;
c40769fee   Jean Delvare   hwmon/lm78: No lo...
958
959
960
  
   exit_unreg_isa_driver:
  	platform_driver_unregister(&lm78_isa_driver);
c40769fee   Jean Delvare   hwmon/lm78: No lo...
961
962
   exit:
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
  }
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
964
  static void lm78_isa_unregister(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
  {
c40769fee   Jean Delvare   hwmon/lm78: No lo...
966
967
968
969
  	if (pdev) {
  		platform_device_unregister(pdev);
  		platform_driver_unregister(&lm78_isa_driver);
  	}
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
  }
  #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;
9b03079fc   Guenter Roeck   hwmon: (lm78) Fix...
986
987
988
989
  	/*
  	 * We register the ISA device first, so that we can skip the
  	 * registration of an I2C interface to the same device.
  	 */
90534c5c5   Jean Delvare   hwmon: (lm78) Mak...
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
  	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
1009
1010
  	i2c_del_driver(&lm78_driver);
  }
7c81c60f3   Jean Delvare   Update Jean Delva...
1011
  MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <jdelvare@suse.de>");
27fe048eb   Jean Delvare   [PATCH] hwmon: ki...
1012
  MODULE_DESCRIPTION("LM78/LM79 driver");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
1016
  MODULE_LICENSE("GPL");
  
  module_init(sm_lm78_init);
  module_exit(sm_lm78_exit);