diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-12 18:54:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-12 18:54:36 -0400 |
commit | 22a4427972af371fddb49c0184a93851ad51070d (patch) | |
tree | 2d513301698bc8c5b691caf7268a57f296fdf69d /drivers/hwmon/sis5595.c | |
parent | 9f02d6b7b43d46a74dd385f06090104ecd0fb807 (diff) | |
parent | ede7fbdf526c314850c9f32dd8da1753bf8d0ad5 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/i2c-2.6
Diffstat (limited to 'drivers/hwmon/sis5595.c')
-rw-r--r-- | drivers/hwmon/sis5595.c | 817 |
1 files changed, 817 insertions, 0 deletions
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c new file mode 100644 index 000000000000..6bbfc8fb4f13 --- /dev/null +++ b/drivers/hwmon/sis5595.c | |||
@@ -0,0 +1,817 @@ | |||
1 | /* | ||
2 | sis5595.c - Part of lm_sensors, Linux kernel modules | ||
3 | for hardware monitoring | ||
4 | |||
5 | Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, | ||
6 | Kyösti Mälkki <kmalkki@cc.hut.fi>, and | ||
7 | Mark D. Studebaker <mdsxyz123@yahoo.com> | ||
8 | Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with | ||
9 | the help of Jean Delvare <khali@linux-fr.org> | ||
10 | |||
11 | This program is free software; you can redistribute it and/or modify | ||
12 | it under the terms of the GNU General Public License as published by | ||
13 | the Free Software Foundation; either version 2 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | SiS southbridge has a LM78-like chip integrated on the same IC. | ||
28 | This driver is a customized copy of lm78.c | ||
29 | |||
30 | Supports following revisions: | ||
31 | Version PCI ID PCI Revision | ||
32 | 1 1039/0008 AF or less | ||
33 | 2 1039/0008 B0 or greater | ||
34 | |||
35 | Note: these chips contain a 0008 device which is incompatible with the | ||
36 | 5595. We recognize these by the presence of the listed | ||
37 | "blacklist" PCI ID and refuse to load. | ||
38 | |||
39 | NOT SUPPORTED PCI ID BLACKLIST PCI ID | ||
40 | 540 0008 0540 | ||
41 | 550 0008 0550 | ||
42 | 5513 0008 5511 | ||
43 | 5581 0008 5597 | ||
44 | 5582 0008 5597 | ||
45 | 5597 0008 5597 | ||
46 | 5598 0008 5597/5598 | ||
47 | 630 0008 0630 | ||
48 | 645 0008 0645 | ||
49 | 730 0008 0730 | ||
50 | 735 0008 0735 | ||
51 | */ | ||
52 | |||
53 | #include <linux/module.h> | ||
54 | #include <linux/slab.h> | ||
55 | #include <linux/ioport.h> | ||
56 | #include <linux/pci.h> | ||
57 | #include <linux/i2c.h> | ||
58 | #include <linux/i2c-sensor.h> | ||
59 | #include <linux/init.h> | ||
60 | #include <linux/jiffies.h> | ||
61 | #include <asm/io.h> | ||
62 | |||
63 | |||
64 | /* If force_addr is set to anything different from 0, we forcibly enable | ||
65 | the device at the given address. */ | ||
66 | static u16 force_addr; | ||
67 | module_param(force_addr, ushort, 0); | ||
68 | MODULE_PARM_DESC(force_addr, | ||
69 | "Initialize the base address of the sensors"); | ||
70 | |||
71 | /* Addresses to scan. | ||
72 | Note that we can't determine the ISA address until we have initialized | ||
73 | our module */ | ||
74 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
75 | static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; | ||
76 | |||
77 | /* Insmod parameters */ | ||
78 | SENSORS_INSMOD_1(sis5595); | ||
79 | |||
80 | /* Many SIS5595 constants specified below */ | ||
81 | |||
82 | /* Length of ISA address segment */ | ||
83 | #define SIS5595_EXTENT 8 | ||
84 | /* PCI Config Registers */ | ||
85 | #define SIS5595_REVISION_REG 0x08 | ||
86 | #define SIS5595_BASE_REG 0x68 | ||
87 | #define SIS5595_PIN_REG 0x7A | ||
88 | #define SIS5595_ENABLE_REG 0x7B | ||
89 | |||
90 | /* Where are the ISA address/data registers relative to the base address */ | ||
91 | #define SIS5595_ADDR_REG_OFFSET 5 | ||
92 | #define SIS5595_DATA_REG_OFFSET 6 | ||
93 | |||
94 | /* The SIS5595 registers */ | ||
95 | #define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) | ||
96 | #define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) | ||
97 | #define SIS5595_REG_IN(nr) (0x20 + (nr)) | ||
98 | |||
99 | #define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr)) | ||
100 | #define SIS5595_REG_FAN(nr) (0x28 + (nr)) | ||
101 | |||
102 | /* On the first version of the chip, the temp registers are separate. | ||
103 | On the second version, | ||
104 | TEMP pin is shared with IN4, configured in PCI register 0x7A. | ||
105 | The registers are the same as well. | ||
106 | OVER and HYST are really MAX and MIN. */ | ||
107 | |||
108 | #define REV2MIN 0xb0 | ||
109 | #define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ | ||
110 | SIS5595_REG_IN(4) : 0x27 | ||
111 | #define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ | ||
112 | SIS5595_REG_IN_MAX(4) : 0x39 | ||
113 | #define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ | ||
114 | SIS5595_REG_IN_MIN(4) : 0x3a | ||
115 | |||
116 | #define SIS5595_REG_CONFIG 0x40 | ||
117 | #define SIS5595_REG_ALARM1 0x41 | ||
118 | #define SIS5595_REG_ALARM2 0x42 | ||
119 | #define SIS5595_REG_FANDIV 0x47 | ||
120 | |||
121 | /* Conversions. Limit checking is only done on the TO_REG | ||
122 | variants. */ | ||
123 | |||
124 | /* IN: mV, (0V to 4.08V) | ||
125 | REG: 16mV/bit */ | ||
126 | static inline u8 IN_TO_REG(unsigned long val) | ||
127 | { | ||
128 | unsigned long nval = SENSORS_LIMIT(val, 0, 4080); | ||
129 | return (nval + 8) / 16; | ||
130 | } | ||
131 | #define IN_FROM_REG(val) ((val) * 16) | ||
132 | |||
133 | static inline u8 FAN_TO_REG(long rpm, int div) | ||
134 | { | ||
135 | if (rpm <= 0) | ||
136 | return 255; | ||
137 | return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); | ||
138 | } | ||
139 | |||
140 | static inline int FAN_FROM_REG(u8 val, int div) | ||
141 | { | ||
142 | return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); | ||
143 | } | ||
144 | |||
145 | /* TEMP: mC (-54.12C to +157.53C) | ||
146 | REG: 0.83C/bit + 52.12, two's complement */ | ||
147 | static inline int TEMP_FROM_REG(s8 val) | ||
148 | { | ||
149 | return val * 830 + 52120; | ||
150 | } | ||
151 | static inline s8 TEMP_TO_REG(int val) | ||
152 | { | ||
153 | int nval = SENSORS_LIMIT(val, -54120, 157530) ; | ||
154 | return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830; | ||
155 | } | ||
156 | |||
157 | /* FAN DIV: 1, 2, 4, or 8 (defaults to 2) | ||
158 | REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ | ||
159 | static inline u8 DIV_TO_REG(int val) | ||
160 | { | ||
161 | return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; | ||
162 | } | ||
163 | #define DIV_FROM_REG(val) (1 << (val)) | ||
164 | |||
165 | /* For the SIS5595, we need to keep some data in memory. That | ||
166 | data is pointed to by sis5595_list[NR]->data. The structure itself is | ||
167 | dynamically allocated, at the time when the new sis5595 client is | ||
168 | allocated. */ | ||
169 | struct sis5595_data { | ||
170 | struct i2c_client client; | ||
171 | struct semaphore lock; | ||
172 | |||
173 | struct semaphore update_lock; | ||
174 | char valid; /* !=0 if following fields are valid */ | ||
175 | unsigned long last_updated; /* In jiffies */ | ||
176 | char maxins; /* == 3 if temp enabled, otherwise == 4 */ | ||
177 | u8 revision; /* Reg. value */ | ||
178 | |||
179 | u8 in[5]; /* Register value */ | ||
180 | u8 in_max[5]; /* Register value */ | ||
181 | u8 in_min[5]; /* Register value */ | ||
182 | u8 fan[2]; /* Register value */ | ||
183 | u8 fan_min[2]; /* Register value */ | ||
184 | s8 temp; /* Register value */ | ||
185 | s8 temp_over; /* Register value */ | ||
186 | s8 temp_hyst; /* Register value */ | ||
187 | u8 fan_div[2]; /* Register encoding, shifted right */ | ||
188 | u16 alarms; /* Register encoding, combined */ | ||
189 | }; | ||
190 | |||
191 | static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ | ||
192 | |||
193 | static int sis5595_attach_adapter(struct i2c_adapter *adapter); | ||
194 | static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind); | ||
195 | static int sis5595_detach_client(struct i2c_client *client); | ||
196 | |||
197 | static int sis5595_read_value(struct i2c_client *client, u8 register); | ||
198 | static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value); | ||
199 | static struct sis5595_data *sis5595_update_device(struct device *dev); | ||
200 | static void sis5595_init_client(struct i2c_client *client); | ||
201 | |||
202 | static struct i2c_driver sis5595_driver = { | ||
203 | .owner = THIS_MODULE, | ||
204 | .name = "sis5595", | ||
205 | .id = I2C_DRIVERID_SIS5595, | ||
206 | .flags = I2C_DF_NOTIFY, | ||
207 | .attach_adapter = sis5595_attach_adapter, | ||
208 | .detach_client = sis5595_detach_client, | ||
209 | }; | ||
210 | |||
211 | /* 4 Voltages */ | ||
212 | static ssize_t show_in(struct device *dev, char *buf, int nr) | ||
213 | { | ||
214 | struct sis5595_data *data = sis5595_update_device(dev); | ||
215 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); | ||
216 | } | ||
217 | |||
218 | static ssize_t show_in_min(struct device *dev, char *buf, int nr) | ||
219 | { | ||
220 | struct sis5595_data *data = sis5595_update_device(dev); | ||
221 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); | ||
222 | } | ||
223 | |||
224 | static ssize_t show_in_max(struct device *dev, char *buf, int nr) | ||
225 | { | ||
226 | struct sis5595_data *data = sis5595_update_device(dev); | ||
227 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); | ||
228 | } | ||
229 | |||
230 | static ssize_t set_in_min(struct device *dev, const char *buf, | ||
231 | size_t count, int nr) | ||
232 | { | ||
233 | struct i2c_client *client = to_i2c_client(dev); | ||
234 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
235 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
236 | |||
237 | down(&data->update_lock); | ||
238 | data->in_min[nr] = IN_TO_REG(val); | ||
239 | sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]); | ||
240 | up(&data->update_lock); | ||
241 | return count; | ||
242 | } | ||
243 | |||
244 | static ssize_t set_in_max(struct device *dev, const char *buf, | ||
245 | size_t count, int nr) | ||
246 | { | ||
247 | struct i2c_client *client = to_i2c_client(dev); | ||
248 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
249 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
250 | |||
251 | down(&data->update_lock); | ||
252 | data->in_max[nr] = IN_TO_REG(val); | ||
253 | sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]); | ||
254 | up(&data->update_lock); | ||
255 | return count; | ||
256 | } | ||
257 | |||
258 | #define show_in_offset(offset) \ | ||
259 | static ssize_t \ | ||
260 | show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
261 | { \ | ||
262 | return show_in(dev, buf, offset); \ | ||
263 | } \ | ||
264 | static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ | ||
265 | show_in##offset, NULL); \ | ||
266 | static ssize_t \ | ||
267 | show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
268 | { \ | ||
269 | return show_in_min(dev, buf, offset); \ | ||
270 | } \ | ||
271 | static ssize_t \ | ||
272 | show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
273 | { \ | ||
274 | return show_in_max(dev, buf, offset); \ | ||
275 | } \ | ||
276 | static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ | ||
277 | const char *buf, size_t count) \ | ||
278 | { \ | ||
279 | return set_in_min(dev, buf, count, offset); \ | ||
280 | } \ | ||
281 | static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ | ||
282 | const char *buf, size_t count) \ | ||
283 | { \ | ||
284 | return set_in_max(dev, buf, count, offset); \ | ||
285 | } \ | ||
286 | static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ | ||
287 | show_in##offset##_min, set_in##offset##_min); \ | ||
288 | static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ | ||
289 | show_in##offset##_max, set_in##offset##_max); | ||
290 | |||
291 | show_in_offset(0); | ||
292 | show_in_offset(1); | ||
293 | show_in_offset(2); | ||
294 | show_in_offset(3); | ||
295 | show_in_offset(4); | ||
296 | |||
297 | /* Temperature */ | ||
298 | static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) | ||
299 | { | ||
300 | struct sis5595_data *data = sis5595_update_device(dev); | ||
301 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); | ||
302 | } | ||
303 | |||
304 | static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) | ||
305 | { | ||
306 | struct sis5595_data *data = sis5595_update_device(dev); | ||
307 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); | ||
308 | } | ||
309 | |||
310 | static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | ||
311 | { | ||
312 | struct i2c_client *client = to_i2c_client(dev); | ||
313 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
314 | long val = simple_strtol(buf, NULL, 10); | ||
315 | |||
316 | down(&data->update_lock); | ||
317 | data->temp_over = TEMP_TO_REG(val); | ||
318 | sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over); | ||
319 | up(&data->update_lock); | ||
320 | return count; | ||
321 | } | ||
322 | |||
323 | static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) | ||
324 | { | ||
325 | struct sis5595_data *data = sis5595_update_device(dev); | ||
326 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); | ||
327 | } | ||
328 | |||
329 | static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | ||
330 | { | ||
331 | struct i2c_client *client = to_i2c_client(dev); | ||
332 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
333 | long val = simple_strtol(buf, NULL, 10); | ||
334 | |||
335 | down(&data->update_lock); | ||
336 | data->temp_hyst = TEMP_TO_REG(val); | ||
337 | sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst); | ||
338 | up(&data->update_lock); | ||
339 | return count; | ||
340 | } | ||
341 | |||
342 | static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); | ||
343 | static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, | ||
344 | show_temp_over, set_temp_over); | ||
345 | static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, | ||
346 | show_temp_hyst, set_temp_hyst); | ||
347 | |||
348 | /* 2 Fans */ | ||
349 | static ssize_t show_fan(struct device *dev, char *buf, int nr) | ||
350 | { | ||
351 | struct sis5595_data *data = sis5595_update_device(dev); | ||
352 | return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], | ||
353 | DIV_FROM_REG(data->fan_div[nr])) ); | ||
354 | } | ||
355 | |||
356 | static ssize_t show_fan_min(struct device *dev, char *buf, int nr) | ||
357 | { | ||
358 | struct sis5595_data *data = sis5595_update_device(dev); | ||
359 | return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], | ||
360 | DIV_FROM_REG(data->fan_div[nr])) ); | ||
361 | } | ||
362 | |||
363 | static ssize_t set_fan_min(struct device *dev, const char *buf, | ||
364 | size_t count, int nr) | ||
365 | { | ||
366 | struct i2c_client *client = to_i2c_client(dev); | ||
367 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
368 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
369 | |||
370 | down(&data->update_lock); | ||
371 | data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); | ||
372 | sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); | ||
373 | up(&data->update_lock); | ||
374 | return count; | ||
375 | } | ||
376 | |||
377 | static ssize_t show_fan_div(struct device *dev, char *buf, int nr) | ||
378 | { | ||
379 | struct sis5595_data *data = sis5595_update_device(dev); | ||
380 | return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); | ||
381 | } | ||
382 | |||
383 | /* Note: we save and restore the fan minimum here, because its value is | ||
384 | determined in part by the fan divisor. This follows the principle of | ||
385 | least suprise; the user doesn't expect the fan minimum to change just | ||
386 | because the divisor changed. */ | ||
387 | static ssize_t set_fan_div(struct device *dev, const char *buf, | ||
388 | size_t count, int nr) | ||
389 | { | ||
390 | struct i2c_client *client = to_i2c_client(dev); | ||
391 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
392 | unsigned long min; | ||
393 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
394 | int reg; | ||
395 | |||
396 | down(&data->update_lock); | ||
397 | min = FAN_FROM_REG(data->fan_min[nr], | ||
398 | DIV_FROM_REG(data->fan_div[nr])); | ||
399 | reg = sis5595_read_value(client, SIS5595_REG_FANDIV); | ||
400 | |||
401 | switch (val) { | ||
402 | case 1: data->fan_div[nr] = 0; break; | ||
403 | case 2: data->fan_div[nr] = 1; break; | ||
404 | case 4: data->fan_div[nr] = 2; break; | ||
405 | case 8: data->fan_div[nr] = 3; break; | ||
406 | default: | ||
407 | dev_err(&client->dev, "fan_div value %ld not " | ||
408 | "supported. Choose one of 1, 2, 4 or 8!\n", val); | ||
409 | up(&data->update_lock); | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | switch (nr) { | ||
414 | case 0: | ||
415 | reg = (reg & 0xcf) | (data->fan_div[nr] << 4); | ||
416 | break; | ||
417 | case 1: | ||
418 | reg = (reg & 0x3f) | (data->fan_div[nr] << 6); | ||
419 | break; | ||
420 | } | ||
421 | sis5595_write_value(client, SIS5595_REG_FANDIV, reg); | ||
422 | data->fan_min[nr] = | ||
423 | FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); | ||
424 | sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); | ||
425 | up(&data->update_lock); | ||
426 | return count; | ||
427 | } | ||
428 | |||
429 | #define show_fan_offset(offset) \ | ||
430 | static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
431 | { \ | ||
432 | return show_fan(dev, buf, offset - 1); \ | ||
433 | } \ | ||
434 | static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
435 | { \ | ||
436 | return show_fan_min(dev, buf, offset - 1); \ | ||
437 | } \ | ||
438 | static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
439 | { \ | ||
440 | return show_fan_div(dev, buf, offset - 1); \ | ||
441 | } \ | ||
442 | static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ | ||
443 | const char *buf, size_t count) \ | ||
444 | { \ | ||
445 | return set_fan_min(dev, buf, count, offset - 1); \ | ||
446 | } \ | ||
447 | static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ | ||
448 | static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ | ||
449 | show_fan_##offset##_min, set_fan_##offset##_min); | ||
450 | |||
451 | show_fan_offset(1); | ||
452 | show_fan_offset(2); | ||
453 | |||
454 | static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf, | ||
455 | size_t count) | ||
456 | { | ||
457 | return set_fan_div(dev, buf, count, 0) ; | ||
458 | } | ||
459 | |||
460 | static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf, | ||
461 | size_t count) | ||
462 | { | ||
463 | return set_fan_div(dev, buf, count, 1) ; | ||
464 | } | ||
465 | static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, | ||
466 | show_fan_1_div, set_fan_1_div); | ||
467 | static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, | ||
468 | show_fan_2_div, set_fan_2_div); | ||
469 | |||
470 | /* Alarms */ | ||
471 | static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) | ||
472 | { | ||
473 | struct sis5595_data *data = sis5595_update_device(dev); | ||
474 | return sprintf(buf, "%d\n", data->alarms); | ||
475 | } | ||
476 | static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); | ||
477 | |||
478 | /* This is called when the module is loaded */ | ||
479 | static int sis5595_attach_adapter(struct i2c_adapter *adapter) | ||
480 | { | ||
481 | if (!(adapter->class & I2C_CLASS_HWMON)) | ||
482 | return 0; | ||
483 | return i2c_detect(adapter, &addr_data, sis5595_detect); | ||
484 | } | ||
485 | |||
486 | int sis5595_detect(struct i2c_adapter *adapter, int address, int kind) | ||
487 | { | ||
488 | int err = 0; | ||
489 | int i; | ||
490 | struct i2c_client *new_client; | ||
491 | struct sis5595_data *data; | ||
492 | char val; | ||
493 | u16 a; | ||
494 | |||
495 | /* Make sure we are probing the ISA bus!! */ | ||
496 | if (!i2c_is_isa_adapter(adapter)) | ||
497 | goto exit; | ||
498 | |||
499 | if (force_addr) | ||
500 | address = force_addr & ~(SIS5595_EXTENT - 1); | ||
501 | /* Reserve the ISA region */ | ||
502 | if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) { | ||
503 | err = -EBUSY; | ||
504 | goto exit; | ||
505 | } | ||
506 | if (force_addr) { | ||
507 | dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address); | ||
508 | if (PCIBIOS_SUCCESSFUL != | ||
509 | pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) | ||
510 | goto exit_release; | ||
511 | if (PCIBIOS_SUCCESSFUL != | ||
512 | pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) | ||
513 | goto exit_release; | ||
514 | if ((a & ~(SIS5595_EXTENT - 1)) != address) | ||
515 | /* doesn't work for some chips? */ | ||
516 | goto exit_release; | ||
517 | } | ||
518 | |||
519 | if (PCIBIOS_SUCCESSFUL != | ||
520 | pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) { | ||
521 | goto exit_release; | ||
522 | } | ||
523 | if ((val & 0x80) == 0) { | ||
524 | if (PCIBIOS_SUCCESSFUL != | ||
525 | pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, | ||
526 | val | 0x80)) | ||
527 | goto exit_release; | ||
528 | if (PCIBIOS_SUCCESSFUL != | ||
529 | pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) | ||
530 | goto exit_release; | ||
531 | if ((val & 0x80) == 0) | ||
532 | /* doesn't work for some chips! */ | ||
533 | goto exit_release; | ||
534 | } | ||
535 | |||
536 | if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) { | ||
537 | err = -ENOMEM; | ||
538 | goto exit_release; | ||
539 | } | ||
540 | memset(data, 0, sizeof(struct sis5595_data)); | ||
541 | |||
542 | new_client = &data->client; | ||
543 | new_client->addr = address; | ||
544 | init_MUTEX(&data->lock); | ||
545 | i2c_set_clientdata(new_client, data); | ||
546 | new_client->adapter = adapter; | ||
547 | new_client->driver = &sis5595_driver; | ||
548 | new_client->flags = 0; | ||
549 | |||
550 | /* Check revision and pin registers to determine whether 4 or 5 voltages */ | ||
551 | pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); | ||
552 | /* 4 voltages, 1 temp */ | ||
553 | data->maxins = 3; | ||
554 | if (data->revision >= REV2MIN) { | ||
555 | pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); | ||
556 | if (!(val & 0x80)) | ||
557 | /* 5 voltages, no temps */ | ||
558 | data->maxins = 4; | ||
559 | } | ||
560 | |||
561 | /* Fill in the remaining client fields and put it into the global list */ | ||
562 | strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE); | ||
563 | |||
564 | data->valid = 0; | ||
565 | init_MUTEX(&data->update_lock); | ||
566 | |||
567 | /* Tell the I2C layer a new client has arrived */ | ||
568 | if ((err = i2c_attach_client(new_client))) | ||
569 | goto exit_free; | ||
570 | |||
571 | /* Initialize the SIS5595 chip */ | ||
572 | sis5595_init_client(new_client); | ||
573 | |||
574 | /* A few vars need to be filled upon startup */ | ||
575 | for (i = 0; i < 2; i++) { | ||
576 | data->fan_min[i] = sis5595_read_value(new_client, | ||
577 | SIS5595_REG_FAN_MIN(i)); | ||
578 | } | ||
579 | |||
580 | /* Register sysfs hooks */ | ||
581 | device_create_file(&new_client->dev, &dev_attr_in0_input); | ||
582 | device_create_file(&new_client->dev, &dev_attr_in0_min); | ||
583 | device_create_file(&new_client->dev, &dev_attr_in0_max); | ||
584 | device_create_file(&new_client->dev, &dev_attr_in1_input); | ||
585 | device_create_file(&new_client->dev, &dev_attr_in1_min); | ||
586 | device_create_file(&new_client->dev, &dev_attr_in1_max); | ||
587 | device_create_file(&new_client->dev, &dev_attr_in2_input); | ||
588 | device_create_file(&new_client->dev, &dev_attr_in2_min); | ||
589 | device_create_file(&new_client->dev, &dev_attr_in2_max); | ||
590 | device_create_file(&new_client->dev, &dev_attr_in3_input); | ||
591 | device_create_file(&new_client->dev, &dev_attr_in3_min); | ||
592 | device_create_file(&new_client->dev, &dev_attr_in3_max); | ||
593 | if (data->maxins == 4) { | ||
594 | device_create_file(&new_client->dev, &dev_attr_in4_input); | ||
595 | device_create_file(&new_client->dev, &dev_attr_in4_min); | ||
596 | device_create_file(&new_client->dev, &dev_attr_in4_max); | ||
597 | } | ||
598 | device_create_file(&new_client->dev, &dev_attr_fan1_input); | ||
599 | device_create_file(&new_client->dev, &dev_attr_fan1_min); | ||
600 | device_create_file(&new_client->dev, &dev_attr_fan1_div); | ||
601 | device_create_file(&new_client->dev, &dev_attr_fan2_input); | ||
602 | device_create_file(&new_client->dev, &dev_attr_fan2_min); | ||
603 | device_create_file(&new_client->dev, &dev_attr_fan2_div); | ||
604 | device_create_file(&new_client->dev, &dev_attr_alarms); | ||
605 | if (data->maxins == 3) { | ||
606 | device_create_file(&new_client->dev, &dev_attr_temp1_input); | ||
607 | device_create_file(&new_client->dev, &dev_attr_temp1_max); | ||
608 | device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); | ||
609 | } | ||
610 | return 0; | ||
611 | |||
612 | exit_free: | ||
613 | kfree(data); | ||
614 | exit_release: | ||
615 | release_region(address, SIS5595_EXTENT); | ||
616 | exit: | ||
617 | return err; | ||
618 | } | ||
619 | |||
620 | static int sis5595_detach_client(struct i2c_client *client) | ||
621 | { | ||
622 | int err; | ||
623 | |||
624 | if ((err = i2c_detach_client(client))) { | ||
625 | dev_err(&client->dev, | ||
626 | "Client deregistration failed, client not detached.\n"); | ||
627 | return err; | ||
628 | } | ||
629 | |||
630 | if (i2c_is_isa_client(client)) | ||
631 | release_region(client->addr, SIS5595_EXTENT); | ||
632 | |||
633 | kfree(i2c_get_clientdata(client)); | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | |||
639 | /* ISA access must be locked explicitly. */ | ||
640 | static int sis5595_read_value(struct i2c_client *client, u8 reg) | ||
641 | { | ||
642 | int res; | ||
643 | |||
644 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
645 | down(&data->lock); | ||
646 | outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); | ||
647 | res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); | ||
648 | up(&data->lock); | ||
649 | return res; | ||
650 | } | ||
651 | |||
652 | static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) | ||
653 | { | ||
654 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
655 | down(&data->lock); | ||
656 | outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); | ||
657 | outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); | ||
658 | up(&data->lock); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | /* Called when we have found a new SIS5595. */ | ||
663 | static void sis5595_init_client(struct i2c_client *client) | ||
664 | { | ||
665 | u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG); | ||
666 | if (!(config & 0x01)) | ||
667 | sis5595_write_value(client, SIS5595_REG_CONFIG, | ||
668 | (config & 0xf7) | 0x01); | ||
669 | } | ||
670 | |||
671 | static struct sis5595_data *sis5595_update_device(struct device *dev) | ||
672 | { | ||
673 | struct i2c_client *client = to_i2c_client(dev); | ||
674 | struct sis5595_data *data = i2c_get_clientdata(client); | ||
675 | int i; | ||
676 | |||
677 | down(&data->update_lock); | ||
678 | |||
679 | if (time_after(jiffies, data->last_updated + HZ + HZ / 2) | ||
680 | || !data->valid) { | ||
681 | |||
682 | for (i = 0; i <= data->maxins; i++) { | ||
683 | data->in[i] = | ||
684 | sis5595_read_value(client, SIS5595_REG_IN(i)); | ||
685 | data->in_min[i] = | ||
686 | sis5595_read_value(client, | ||
687 | SIS5595_REG_IN_MIN(i)); | ||
688 | data->in_max[i] = | ||
689 | sis5595_read_value(client, | ||
690 | SIS5595_REG_IN_MAX(i)); | ||
691 | } | ||
692 | for (i = 0; i < 2; i++) { | ||
693 | data->fan[i] = | ||
694 | sis5595_read_value(client, SIS5595_REG_FAN(i)); | ||
695 | data->fan_min[i] = | ||
696 | sis5595_read_value(client, | ||
697 | SIS5595_REG_FAN_MIN(i)); | ||
698 | } | ||
699 | if (data->maxins == 3) { | ||
700 | data->temp = | ||
701 | sis5595_read_value(client, SIS5595_REG_TEMP); | ||
702 | data->temp_over = | ||
703 | sis5595_read_value(client, SIS5595_REG_TEMP_OVER); | ||
704 | data->temp_hyst = | ||
705 | sis5595_read_value(client, SIS5595_REG_TEMP_HYST); | ||
706 | } | ||
707 | i = sis5595_read_value(client, SIS5595_REG_FANDIV); | ||
708 | data->fan_div[0] = (i >> 4) & 0x03; | ||
709 | data->fan_div[1] = i >> 6; | ||
710 | data->alarms = | ||
711 | sis5595_read_value(client, SIS5595_REG_ALARM1) | | ||
712 | (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); | ||
713 | data->last_updated = jiffies; | ||
714 | data->valid = 1; | ||
715 | } | ||
716 | |||
717 | up(&data->update_lock); | ||
718 | |||
719 | return data; | ||
720 | } | ||
721 | |||
722 | static struct pci_device_id sis5595_pci_ids[] = { | ||
723 | { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, | ||
724 | { 0, } | ||
725 | }; | ||
726 | |||
727 | MODULE_DEVICE_TABLE(pci, sis5595_pci_ids); | ||
728 | |||
729 | static int blacklist[] __devinitdata = { | ||
730 | PCI_DEVICE_ID_SI_540, | ||
731 | PCI_DEVICE_ID_SI_550, | ||
732 | PCI_DEVICE_ID_SI_630, | ||
733 | PCI_DEVICE_ID_SI_645, | ||
734 | PCI_DEVICE_ID_SI_730, | ||
735 | PCI_DEVICE_ID_SI_735, | ||
736 | PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but | ||
737 | that ID shows up in other chips so we | ||
738 | use the 5511 ID for recognition */ | ||
739 | PCI_DEVICE_ID_SI_5597, | ||
740 | PCI_DEVICE_ID_SI_5598, | ||
741 | 0 }; | ||
742 | |||
743 | static int __devinit sis5595_pci_probe(struct pci_dev *dev, | ||
744 | const struct pci_device_id *id) | ||
745 | { | ||
746 | u16 val; | ||
747 | int *i; | ||
748 | int addr = 0; | ||
749 | |||
750 | for (i = blacklist; *i != 0; i++) { | ||
751 | struct pci_dev *dev; | ||
752 | dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); | ||
753 | if (dev) { | ||
754 | dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); | ||
755 | pci_dev_put(dev); | ||
756 | return -ENODEV; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | if (PCIBIOS_SUCCESSFUL != | ||
761 | pci_read_config_word(dev, SIS5595_BASE_REG, &val)) | ||
762 | return -ENODEV; | ||
763 | |||
764 | addr = val & ~(SIS5595_EXTENT - 1); | ||
765 | if (addr == 0 && force_addr == 0) { | ||
766 | dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); | ||
767 | return -ENODEV; | ||
768 | } | ||
769 | if (force_addr) | ||
770 | addr = force_addr; /* so detect will get called */ | ||
771 | |||
772 | if (!addr) { | ||
773 | dev_err(&dev->dev,"No SiS 5595 sensors found.\n"); | ||
774 | return -ENODEV; | ||
775 | } | ||
776 | normal_isa[0] = addr; | ||
777 | |||
778 | s_bridge = pci_dev_get(dev); | ||
779 | if (i2c_add_driver(&sis5595_driver)) { | ||
780 | pci_dev_put(s_bridge); | ||
781 | s_bridge = NULL; | ||
782 | } | ||
783 | |||
784 | /* Always return failure here. This is to allow other drivers to bind | ||
785 | * to this pci device. We don't really want to have control over the | ||
786 | * pci device, we only wanted to read as few register values from it. | ||
787 | */ | ||
788 | return -ENODEV; | ||
789 | } | ||
790 | |||
791 | static struct pci_driver sis5595_pci_driver = { | ||
792 | .name = "sis5595", | ||
793 | .id_table = sis5595_pci_ids, | ||
794 | .probe = sis5595_pci_probe, | ||
795 | }; | ||
796 | |||
797 | static int __init sm_sis5595_init(void) | ||
798 | { | ||
799 | return pci_register_driver(&sis5595_pci_driver); | ||
800 | } | ||
801 | |||
802 | static void __exit sm_sis5595_exit(void) | ||
803 | { | ||
804 | pci_unregister_driver(&sis5595_pci_driver); | ||
805 | if (s_bridge != NULL) { | ||
806 | i2c_del_driver(&sis5595_driver); | ||
807 | pci_dev_put(s_bridge); | ||
808 | s_bridge = NULL; | ||
809 | } | ||
810 | } | ||
811 | |||
812 | MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); | ||
813 | MODULE_DESCRIPTION("SiS 5595 Sensor device"); | ||
814 | MODULE_LICENSE("GPL"); | ||
815 | |||
816 | module_init(sm_sis5595_init); | ||
817 | module_exit(sm_sis5595_exit); | ||