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