blob: 3f400263fc0f2e861b88614634e86b23264a898c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Jean Delvarefde09502005-07-19 23:51:07 +020058#include <linux/i2c-isa.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040059#include <linux/hwmon.h>
60#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#include <linux/init.h>
Dominik Hacklff324092005-05-16 18:12:18 +020062#include <linux/jiffies.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010063#include <linux/mutex.h>
Jean Delvarea5ebe662006-09-24 21:24:46 +020064#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <asm/io.h>
66
67
68/* If force_addr is set to anything different from 0, we forcibly enable
69 the device at the given address. */
70static u16 force_addr;
71module_param(force_addr, ushort, 0);
72MODULE_PARM_DESC(force_addr,
73 "Initialize the base address of the sensors");
74
Jean Delvare2d8672c2005-07-19 23:56:35 +020075/* Device address
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 Note that we can't determine the ISA address until we have initialized
77 our module */
Jean Delvare2d8672c2005-07-19 23:56:35 +020078static unsigned short address;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
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 */
126static 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
133static 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
140static 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 */
147static inline int TEMP_FROM_REG(s8 val)
148{
149 return val * 830 + 52120;
150}
151static 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) */
159static 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
Jean Delvareed6bafb2007-02-14 21:15:03 +0100165/* For each registered chip, we need to keep some data in memory.
166 The structure is dynamically allocated. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167struct sis5595_data {
168 struct i2c_client client;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400169 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100170 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100172 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 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
190static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */
191
Jean Delvare2d8672c2005-07-19 23:56:35 +0200192static int sis5595_detect(struct i2c_adapter *adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193static int sis5595_detach_client(struct i2c_client *client);
194
Darren Jenkinsf6c27fc2006-02-27 23:14:58 +0100195static int sis5595_read_value(struct i2c_client *client, u8 reg);
196static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197static struct sis5595_data *sis5595_update_device(struct device *dev);
198static void sis5595_init_client(struct i2c_client *client);
199
200static struct i2c_driver sis5595_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100201 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200202 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100203 .name = "sis5595",
204 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200205 .attach_adapter = sis5595_detect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .detach_client = sis5595_detach_client,
207};
208
209/* 4 Voltages */
210static ssize_t show_in(struct device *dev, char *buf, int nr)
211{
212 struct sis5595_data *data = sis5595_update_device(dev);
213 return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
214}
215
216static ssize_t show_in_min(struct device *dev, char *buf, int nr)
217{
218 struct sis5595_data *data = sis5595_update_device(dev);
219 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
220}
221
222static ssize_t show_in_max(struct device *dev, char *buf, int nr)
223{
224 struct sis5595_data *data = sis5595_update_device(dev);
225 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
226}
227
228static ssize_t set_in_min(struct device *dev, const char *buf,
229 size_t count, int nr)
230{
231 struct i2c_client *client = to_i2c_client(dev);
232 struct sis5595_data *data = i2c_get_clientdata(client);
233 unsigned long val = simple_strtoul(buf, NULL, 10);
234
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100235 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 data->in_min[nr] = IN_TO_REG(val);
237 sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100238 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return count;
240}
241
242static ssize_t set_in_max(struct device *dev, const char *buf,
243 size_t count, int nr)
244{
245 struct i2c_client *client = to_i2c_client(dev);
246 struct sis5595_data *data = i2c_get_clientdata(client);
247 unsigned long val = simple_strtoul(buf, NULL, 10);
248
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100249 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 data->in_max[nr] = IN_TO_REG(val);
251 sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100252 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 return count;
254}
255
256#define show_in_offset(offset) \
257static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400258 show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{ \
260 return show_in(dev, buf, offset); \
261} \
262static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
263 show_in##offset, NULL); \
264static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400265 show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{ \
267 return show_in_min(dev, buf, offset); \
268} \
269static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400270 show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{ \
272 return show_in_max(dev, buf, offset); \
273} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400274static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 const char *buf, size_t count) \
276{ \
277 return set_in_min(dev, buf, count, offset); \
278} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400279static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 const char *buf, size_t count) \
281{ \
282 return set_in_max(dev, buf, count, offset); \
283} \
284static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
285 show_in##offset##_min, set_in##offset##_min); \
286static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
287 show_in##offset##_max, set_in##offset##_max);
288
289show_in_offset(0);
290show_in_offset(1);
291show_in_offset(2);
292show_in_offset(3);
293show_in_offset(4);
294
295/* Temperature */
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400296static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
298 struct sis5595_data *data = sis5595_update_device(dev);
299 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
300}
301
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400302static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
304 struct sis5595_data *data = sis5595_update_device(dev);
305 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
306}
307
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400308static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309{
310 struct i2c_client *client = to_i2c_client(dev);
311 struct sis5595_data *data = i2c_get_clientdata(client);
312 long val = simple_strtol(buf, NULL, 10);
313
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100314 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 data->temp_over = TEMP_TO_REG(val);
316 sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100317 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 return count;
319}
320
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400321static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
323 struct sis5595_data *data = sis5595_update_device(dev);
324 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
325}
326
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400327static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328{
329 struct i2c_client *client = to_i2c_client(dev);
330 struct sis5595_data *data = i2c_get_clientdata(client);
331 long val = simple_strtol(buf, NULL, 10);
332
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100333 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 data->temp_hyst = TEMP_TO_REG(val);
335 sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100336 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 return count;
338}
339
340static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
341static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
342 show_temp_over, set_temp_over);
343static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
344 show_temp_hyst, set_temp_hyst);
345
346/* 2 Fans */
347static ssize_t show_fan(struct device *dev, char *buf, int nr)
348{
349 struct sis5595_data *data = sis5595_update_device(dev);
350 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
351 DIV_FROM_REG(data->fan_div[nr])) );
352}
353
354static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
355{
356 struct sis5595_data *data = sis5595_update_device(dev);
357 return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
358 DIV_FROM_REG(data->fan_div[nr])) );
359}
360
361static ssize_t set_fan_min(struct device *dev, const char *buf,
362 size_t count, int nr)
363{
364 struct i2c_client *client = to_i2c_client(dev);
365 struct sis5595_data *data = i2c_get_clientdata(client);
366 unsigned long val = simple_strtoul(buf, NULL, 10);
367
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100368 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
370 sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100371 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 return count;
373}
374
375static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
376{
377 struct sis5595_data *data = sis5595_update_device(dev);
378 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
379}
380
381/* Note: we save and restore the fan minimum here, because its value is
382 determined in part by the fan divisor. This follows the principle of
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200383 least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 because the divisor changed. */
385static ssize_t set_fan_div(struct device *dev, const char *buf,
386 size_t count, int nr)
387{
388 struct i2c_client *client = to_i2c_client(dev);
389 struct sis5595_data *data = i2c_get_clientdata(client);
390 unsigned long min;
391 unsigned long val = simple_strtoul(buf, NULL, 10);
392 int reg;
393
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100394 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 min = FAN_FROM_REG(data->fan_min[nr],
396 DIV_FROM_REG(data->fan_div[nr]));
397 reg = sis5595_read_value(client, SIS5595_REG_FANDIV);
398
399 switch (val) {
400 case 1: data->fan_div[nr] = 0; break;
401 case 2: data->fan_div[nr] = 1; break;
402 case 4: data->fan_div[nr] = 2; break;
403 case 8: data->fan_div[nr] = 3; break;
404 default:
405 dev_err(&client->dev, "fan_div value %ld not "
406 "supported. Choose one of 1, 2, 4 or 8!\n", val);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100407 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return -EINVAL;
409 }
410
411 switch (nr) {
412 case 0:
413 reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
414 break;
415 case 1:
416 reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
417 break;
418 }
419 sis5595_write_value(client, SIS5595_REG_FANDIV, reg);
420 data->fan_min[nr] =
421 FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
422 sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100423 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return count;
425}
426
427#define show_fan_offset(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400428static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{ \
430 return show_fan(dev, buf, offset - 1); \
431} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400432static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433{ \
434 return show_fan_min(dev, buf, offset - 1); \
435} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400436static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{ \
438 return show_fan_div(dev, buf, offset - 1); \
439} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400440static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 const char *buf, size_t count) \
442{ \
443 return set_fan_min(dev, buf, count, offset - 1); \
444} \
445static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
446static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
447 show_fan_##offset##_min, set_fan_##offset##_min);
448
449show_fan_offset(1);
450show_fan_offset(2);
451
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400452static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 size_t count)
454{
455 return set_fan_div(dev, buf, count, 0) ;
456}
457
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400458static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 size_t count)
460{
461 return set_fan_div(dev, buf, count, 1) ;
462}
463static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
464 show_fan_1_div, set_fan_1_div);
465static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
466 show_fan_2_div, set_fan_2_div);
467
468/* Alarms */
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400469static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
471 struct sis5595_data *data = sis5595_update_device(dev);
472 return sprintf(buf, "%d\n", data->alarms);
473}
474static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
Jean Delvarea5ebe662006-09-24 21:24:46 +0200475
476static struct attribute *sis5595_attributes[] = {
477 &dev_attr_in0_input.attr,
478 &dev_attr_in0_min.attr,
479 &dev_attr_in0_max.attr,
480 &dev_attr_in1_input.attr,
481 &dev_attr_in1_min.attr,
482 &dev_attr_in1_max.attr,
483 &dev_attr_in2_input.attr,
484 &dev_attr_in2_min.attr,
485 &dev_attr_in2_max.attr,
486 &dev_attr_in3_input.attr,
487 &dev_attr_in3_min.attr,
488 &dev_attr_in3_max.attr,
489
490 &dev_attr_fan1_input.attr,
491 &dev_attr_fan1_min.attr,
492 &dev_attr_fan1_div.attr,
493 &dev_attr_fan2_input.attr,
494 &dev_attr_fan2_min.attr,
495 &dev_attr_fan2_div.attr,
496
497 &dev_attr_alarms.attr,
498 NULL
499};
500
501static const struct attribute_group sis5595_group = {
502 .attrs = sis5595_attributes,
503};
504
505static struct attribute *sis5595_attributes_opt[] = {
506 &dev_attr_in4_input.attr,
507 &dev_attr_in4_min.attr,
508 &dev_attr_in4_max.attr,
509
510 &dev_attr_temp1_input.attr,
511 &dev_attr_temp1_max.attr,
512 &dev_attr_temp1_max_hyst.attr,
513 NULL
514};
515
516static const struct attribute_group sis5595_group_opt = {
517 .attrs = sis5595_attributes_opt,
518};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
520/* This is called when the module is loaded */
Jean Delvare2d8672c2005-07-19 23:56:35 +0200521static int sis5595_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
523 int err = 0;
524 int i;
525 struct i2c_client *new_client;
526 struct sis5595_data *data;
527 char val;
528 u16 a;
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (force_addr)
531 address = force_addr & ~(SIS5595_EXTENT - 1);
532 /* Reserve the ISA region */
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100533 if (!request_region(address, SIS5595_EXTENT,
534 sis5595_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 err = -EBUSY;
536 goto exit;
537 }
538 if (force_addr) {
539 dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address);
540 if (PCIBIOS_SUCCESSFUL !=
541 pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
542 goto exit_release;
543 if (PCIBIOS_SUCCESSFUL !=
544 pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
545 goto exit_release;
546 if ((a & ~(SIS5595_EXTENT - 1)) != address)
547 /* doesn't work for some chips? */
548 goto exit_release;
549 }
550
551 if (PCIBIOS_SUCCESSFUL !=
552 pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) {
553 goto exit_release;
554 }
555 if ((val & 0x80) == 0) {
556 if (PCIBIOS_SUCCESSFUL !=
557 pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
558 val | 0x80))
559 goto exit_release;
560 if (PCIBIOS_SUCCESSFUL !=
561 pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
562 goto exit_release;
563 if ((val & 0x80) == 0)
564 /* doesn't work for some chips! */
565 goto exit_release;
566 }
567
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200568 if (!(data = kzalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 err = -ENOMEM;
570 goto exit_release;
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
573 new_client = &data->client;
574 new_client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100575 mutex_init(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 i2c_set_clientdata(new_client, data);
577 new_client->adapter = adapter;
578 new_client->driver = &sis5595_driver;
579 new_client->flags = 0;
580
581 /* Check revision and pin registers to determine whether 4 or 5 voltages */
582 pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
583 /* 4 voltages, 1 temp */
584 data->maxins = 3;
585 if (data->revision >= REV2MIN) {
586 pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
587 if (!(val & 0x80))
588 /* 5 voltages, no temps */
589 data->maxins = 4;
590 }
591
592 /* Fill in the remaining client fields and put it into the global list */
593 strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE);
594
595 data->valid = 0;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100596 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
598 /* Tell the I2C layer a new client has arrived */
599 if ((err = i2c_attach_client(new_client)))
600 goto exit_free;
601
602 /* Initialize the SIS5595 chip */
603 sis5595_init_client(new_client);
604
605 /* A few vars need to be filled upon startup */
606 for (i = 0; i < 2; i++) {
607 data->fan_min[i] = sis5595_read_value(new_client,
608 SIS5595_REG_FAN_MIN(i));
609 }
610
611 /* Register sysfs hooks */
Jean Delvarea5ebe662006-09-24 21:24:46 +0200612 if ((err = sysfs_create_group(&new_client->dev.kobj, &sis5595_group)))
613 goto exit_detach;
614 if (data->maxins == 4) {
615 if ((err = device_create_file(&new_client->dev,
616 &dev_attr_in4_input))
617 || (err = device_create_file(&new_client->dev,
618 &dev_attr_in4_min))
619 || (err = device_create_file(&new_client->dev,
620 &dev_attr_in4_max)))
621 goto exit_remove_files;
622 } else {
623 if ((err = device_create_file(&new_client->dev,
624 &dev_attr_temp1_input))
625 || (err = device_create_file(&new_client->dev,
626 &dev_attr_temp1_max))
627 || (err = device_create_file(&new_client->dev,
628 &dev_attr_temp1_max_hyst)))
629 goto exit_remove_files;
630 }
631
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400632 data->class_dev = hwmon_device_register(&new_client->dev);
633 if (IS_ERR(data->class_dev)) {
634 err = PTR_ERR(data->class_dev);
Jean Delvarea5ebe662006-09-24 21:24:46 +0200635 goto exit_remove_files;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400636 }
637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 return 0;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400639
Jean Delvarea5ebe662006-09-24 21:24:46 +0200640exit_remove_files:
641 sysfs_remove_group(&new_client->dev.kobj, &sis5595_group);
642 sysfs_remove_group(&new_client->dev.kobj, &sis5595_group_opt);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400643exit_detach:
644 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645exit_free:
646 kfree(data);
647exit_release:
648 release_region(address, SIS5595_EXTENT);
649exit:
650 return err;
651}
652
653static int sis5595_detach_client(struct i2c_client *client)
654{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400655 struct sis5595_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 int err;
657
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400658 hwmon_device_unregister(data->class_dev);
Jean Delvarea5ebe662006-09-24 21:24:46 +0200659 sysfs_remove_group(&client->dev.kobj, &sis5595_group);
660 sysfs_remove_group(&client->dev.kobj, &sis5595_group_opt);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400661
Jean Delvare7bef5592005-07-27 22:14:49 +0200662 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Jean Delvare2d8672c2005-07-19 23:56:35 +0200665 release_region(client->addr, SIS5595_EXTENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400667 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 return 0;
670}
671
672
673/* ISA access must be locked explicitly. */
674static int sis5595_read_value(struct i2c_client *client, u8 reg)
675{
676 int res;
677
678 struct sis5595_data *data = i2c_get_clientdata(client);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100679 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
681 res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100682 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return res;
684}
685
686static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
687{
688 struct sis5595_data *data = i2c_get_clientdata(client);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100689 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
691 outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100692 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return 0;
694}
695
696/* Called when we have found a new SIS5595. */
697static void sis5595_init_client(struct i2c_client *client)
698{
699 u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG);
700 if (!(config & 0x01))
701 sis5595_write_value(client, SIS5595_REG_CONFIG,
702 (config & 0xf7) | 0x01);
703}
704
705static struct sis5595_data *sis5595_update_device(struct device *dev)
706{
707 struct i2c_client *client = to_i2c_client(dev);
708 struct sis5595_data *data = i2c_get_clientdata(client);
709 int i;
710
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100711 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
713 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
714 || !data->valid) {
715
716 for (i = 0; i <= data->maxins; i++) {
717 data->in[i] =
718 sis5595_read_value(client, SIS5595_REG_IN(i));
719 data->in_min[i] =
720 sis5595_read_value(client,
721 SIS5595_REG_IN_MIN(i));
722 data->in_max[i] =
723 sis5595_read_value(client,
724 SIS5595_REG_IN_MAX(i));
725 }
726 for (i = 0; i < 2; i++) {
727 data->fan[i] =
728 sis5595_read_value(client, SIS5595_REG_FAN(i));
729 data->fan_min[i] =
730 sis5595_read_value(client,
731 SIS5595_REG_FAN_MIN(i));
732 }
733 if (data->maxins == 3) {
734 data->temp =
735 sis5595_read_value(client, SIS5595_REG_TEMP);
736 data->temp_over =
737 sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
738 data->temp_hyst =
739 sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
740 }
741 i = sis5595_read_value(client, SIS5595_REG_FANDIV);
742 data->fan_div[0] = (i >> 4) & 0x03;
743 data->fan_div[1] = i >> 6;
744 data->alarms =
745 sis5595_read_value(client, SIS5595_REG_ALARM1) |
746 (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
747 data->last_updated = jiffies;
748 data->valid = 1;
749 }
750
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100751 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 return data;
754}
755
756static struct pci_device_id sis5595_pci_ids[] = {
757 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
758 { 0, }
759};
760
761MODULE_DEVICE_TABLE(pci, sis5595_pci_ids);
762
763static int blacklist[] __devinitdata = {
764 PCI_DEVICE_ID_SI_540,
765 PCI_DEVICE_ID_SI_550,
766 PCI_DEVICE_ID_SI_630,
767 PCI_DEVICE_ID_SI_645,
768 PCI_DEVICE_ID_SI_730,
769 PCI_DEVICE_ID_SI_735,
770 PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
771 that ID shows up in other chips so we
772 use the 5511 ID for recognition */
773 PCI_DEVICE_ID_SI_5597,
774 PCI_DEVICE_ID_SI_5598,
775 0 };
776
777static int __devinit sis5595_pci_probe(struct pci_dev *dev,
778 const struct pci_device_id *id)
779{
780 u16 val;
781 int *i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 for (i = blacklist; *i != 0; i++) {
784 struct pci_dev *dev;
785 dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
786 if (dev) {
787 dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
788 pci_dev_put(dev);
789 return -ENODEV;
790 }
791 }
792
793 if (PCIBIOS_SUCCESSFUL !=
794 pci_read_config_word(dev, SIS5595_BASE_REG, &val))
795 return -ENODEV;
796
Jean Delvare2d8672c2005-07-19 23:56:35 +0200797 address = val & ~(SIS5595_EXTENT - 1);
798 if (address == 0 && force_addr == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n");
800 return -ENODEV;
801 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 s_bridge = pci_dev_get(dev);
Jean Delvarefde09502005-07-19 23:51:07 +0200804 if (i2c_isa_add_driver(&sis5595_driver)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 pci_dev_put(s_bridge);
806 s_bridge = NULL;
807 }
808
809 /* Always return failure here. This is to allow other drivers to bind
810 * to this pci device. We don't really want to have control over the
811 * pci device, we only wanted to read as few register values from it.
812 */
813 return -ENODEV;
814}
815
816static struct pci_driver sis5595_pci_driver = {
817 .name = "sis5595",
818 .id_table = sis5595_pci_ids,
819 .probe = sis5595_pci_probe,
820};
821
822static int __init sm_sis5595_init(void)
823{
824 return pci_register_driver(&sis5595_pci_driver);
825}
826
827static void __exit sm_sis5595_exit(void)
828{
829 pci_unregister_driver(&sis5595_pci_driver);
830 if (s_bridge != NULL) {
Jean Delvarefde09502005-07-19 23:51:07 +0200831 i2c_isa_del_driver(&sis5595_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 pci_dev_put(s_bridge);
833 s_bridge = NULL;
834 }
835}
836
837MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
838MODULE_DESCRIPTION("SiS 5595 Sensor device");
839MODULE_LICENSE("GPL");
840
841module_init(sm_sis5595_init);
842module_exit(sm_sis5595_exit);