blob: 0ee9a278537a419602a4f2f4bcca85668dfb2548 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 via686a.c - Part of lm_sensors, Linux kernel modules
Denis Vlasenko6328c0e2005-06-22 10:25:13 +03003 for hardware monitoring
Jean Delvarebe8992c2005-05-16 19:00:52 +02004
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Denis Vlasenko6328c0e2005-06-22 10:25:13 +03006 Kyösti Mälkki <kmalkki@cc.hut.fi>,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 Mark Studebaker <mdsxyz123@yahoo.com>,
8 and Bob Dougherty <bobd@stanford.edu>
Jean Delvarebe8992c2005-05-16 19:00:52 +02009 (Some conversion-factor data were contributed by Jonathan Teh Soon Yew
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25*/
26
27/*
28 Supports the Via VT82C686A, VT82C686B south bridges.
29 Reports all as a 686A.
30 Warning - only supports a single device.
31*/
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/module.h>
34#include <linux/slab.h>
35#include <linux/pci.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/jiffies.h>
Jean Delvare2ec342e2007-06-09 10:11:16 -040037#include <linux/platform_device.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040038#include <linux/hwmon.h>
39#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010041#include <linux/mutex.h>
Jean Delvarea5ebe662006-09-24 21:24:46 +020042#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <asm/io.h>
44
45
46/* If force_addr is set to anything different from 0, we forcibly enable
47 the device at the given address. */
Jean Delvare02002962005-09-25 16:26:44 +020048static unsigned short force_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049module_param(force_addr, ushort, 0);
50MODULE_PARM_DESC(force_addr,
51 "Initialize the base address of the sensors");
52
Jean Delvare2ec342e2007-06-09 10:11:16 -040053static struct platform_device *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
55/*
56 The Via 686a southbridge has a LM78-like chip integrated on the same IC.
57 This driver is a customized copy of lm78.c
58*/
59
60/* Many VIA686A constants specified below */
61
62/* Length of ISA address segment */
Jean Delvarebe8992c2005-05-16 19:00:52 +020063#define VIA686A_EXTENT 0x80
64#define VIA686A_BASE_REG 0x70
65#define VIA686A_ENABLE_REG 0x74
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67/* The VIA686A registers */
68/* ins numbered 0-4 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020069#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
70#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
71#define VIA686A_REG_IN(nr) (0x22 + (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73/* fans numbered 1-2 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020074#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
75#define VIA686A_REG_FAN(nr) (0x28 + (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
Linus Torvalds1da177e2005-04-16 15:20:36 -070077/* temps numbered 1-3 */
Jean Delvare563db2f2005-05-17 22:38:57 +020078static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f };
79static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d };
80static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e };
Jean Delvarebe8992c2005-05-16 19:00:52 +020081/* bits 7-6 */
82#define VIA686A_REG_TEMP_LOW1 0x4b
83/* 2 = bits 5-4, 3 = bits 7-6 */
84#define VIA686A_REG_TEMP_LOW23 0x49
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Jean Delvarebe8992c2005-05-16 19:00:52 +020086#define VIA686A_REG_ALARM1 0x41
87#define VIA686A_REG_ALARM2 0x42
88#define VIA686A_REG_FANDIV 0x47
89#define VIA686A_REG_CONFIG 0x40
90/* The following register sets temp interrupt mode (bits 1-0 for temp1,
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 3-2 for temp2, 5-4 for temp3). Modes are:
92 00 interrupt stays as long as value is out-of-range
93 01 interrupt is cleared once register is read (default)
94 10 comparator mode- like 00, but ignores hysteresis
95 11 same as 00 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020096#define VIA686A_REG_TEMP_MODE 0x4b
Linus Torvalds1da177e2005-04-16 15:20:36 -070097/* We'll just assume that you want to set all 3 simultaneously: */
Jean Delvarebe8992c2005-05-16 19:00:52 +020098#define VIA686A_TEMP_MODE_MASK 0x3F
99#define VIA686A_TEMP_MODE_CONTINUOUS 0x00
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101/* Conversions. Limit checking is only done on the TO_REG
Jean Delvarebe8992c2005-05-16 19:00:52 +0200102 variants.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
104********* VOLTAGE CONVERSIONS (Bob Dougherty) ********
105 From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
106 voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp
107 voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V
108 voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V
109 voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V
110 voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V
111 in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
112 That is:
113 volts = (25*regVal+133)*factor
114 regVal = (volts/factor-133)/25
Jean Delvarebe8992c2005-05-16 19:00:52 +0200115 (These conversions were contributed by Jonathan Teh Soon Yew
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 <j.teh@iname.com>) */
117static inline u8 IN_TO_REG(long val, int inNum)
118{
119 /* To avoid floating point, we multiply constants by 10 (100 for +12V).
120 Rounding is done (120500 is actually 133000 - 12500).
121 Remember that val is expressed in 0.001V/bit, which is why we divide
122 by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
123 for the constants. */
124 if (inNum <= 1)
125 return (u8)
126 SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255);
127 else if (inNum == 2)
128 return (u8)
129 SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255);
130 else if (inNum == 3)
131 return (u8)
132 SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255);
133 else
134 return (u8)
135 SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255);
136}
137
138static inline long IN_FROM_REG(u8 val, int inNum)
139{
140 /* To avoid floating point, we multiply constants by 10 (100 for +12V).
141 We also multiply them by 1000 because we want 0.001V/bit for the
142 output value. Rounding is done. */
143 if (inNum <= 1)
144 return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
145 else if (inNum == 2)
146 return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
147 else if (inNum == 3)
148 return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
149 else
150 return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
151}
152
153/********* FAN RPM CONVERSIONS ********/
154/* Higher register values = slower fans (the fan's strobe gates a counter).
155 But this chip saturates back at 0, not at 255 like all the other chips.
156 So, 0 means 0 RPM */
157static inline u8 FAN_TO_REG(long rpm, int div)
158{
159 if (rpm == 0)
160 return 0;
161 rpm = SENSORS_LIMIT(rpm, 1, 1000000);
162 return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
163}
164
165#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
166
167/******** TEMP CONVERSIONS (Bob Dougherty) *********/
168/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
169 if(temp<169)
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300170 return double(temp)*0.427-32.08;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 else if(temp>=169 && temp<=202)
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300172 return double(temp)*0.582-58.16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 else
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300174 return double(temp)*0.924-127.33;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Jean Delvarebe8992c2005-05-16 19:00:52 +0200176 A fifth-order polynomial fits the unofficial data (provided by Alex van
177 Kaam <darkside@chello.nl>) a bit better. It also give more reasonable
178 numbers on my machine (ie. they agree with what my BIOS tells me).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 Here's the fifth-order fit to the 8-bit data:
Jean Delvarebe8992c2005-05-16 19:00:52 +0200180 temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300181 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Jean Delvarebe8992c2005-05-16 19:00:52 +0200183 (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 finding my typos in this formula!)
185
Jean Delvarebe8992c2005-05-16 19:00:52 +0200186 Alas, none of the elegant function-fit solutions will work because we
187 aren't allowed to use floating point in the kernel and doing it with
188 integers doesn't provide enough precision. So we'll do boring old
189 look-up table stuff. The unofficial data (see below) have effectively
190 7-bit resolution (they are rounded to the nearest degree). I'm assuming
191 that the transfer function of the device is monotonic and smooth, so a
192 smooth function fit to the data will allow us to get better precision.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 I used the 5th-order poly fit described above and solved for
Jean Delvarebe8992c2005-05-16 19:00:52 +0200194 VIA register values 0-255. I *10 before rounding, so we get tenth-degree
195 precision. (I could have done all 1024 values for our 10-bit readings,
196 but the function is very linear in the useful range (0-80 deg C), so
197 we'll just use linear interpolation for 10-bit readings.) So, tempLUT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 is the temp at via register values 0-255: */
Jean Delvare088341b2005-09-10 23:00:46 +0200199static const s16 tempLUT[] =
Jean Delvarebe8992c2005-05-16 19:00:52 +0200200{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
201 -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
202 -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
203 -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
204 -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
205 -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
206 -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
207 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
208 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
209 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
210 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
211 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
212 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
213 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
214 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
215 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
216 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
217 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
218 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
219 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
220 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
221 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222};
223
Jean Delvarebe8992c2005-05-16 19:00:52 +0200224/* the original LUT values from Alex van Kaam <darkside@chello.nl>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 (for via register values 12-240):
226{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
227-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
228-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
229-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
23012,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
23122,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
23233,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
23345,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
23461,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
23585,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
236
237
238 Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed
Jean Delvarebe8992c2005-05-16 19:00:52 +0200239 an extra term for a good fit to these inverse data!) and then
240 solving for each temp value from -50 to 110 (the useable range for
241 this chip). Here's the fit:
242 viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
244 Note that n=161: */
245static const u8 viaLUT[] =
Jean Delvarebe8992c2005-05-16 19:00:52 +0200246{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
247 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
248 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
249 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
250 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
251 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
252 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
253 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
254 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
255 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
256 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
257 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
258 239, 240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259};
260
261/* Converting temps to (8-bit) hyst and over registers
262 No interpolation here.
263 The +50 is because the temps start at -50 */
264static inline u8 TEMP_TO_REG(long val)
265{
Jean Delvarebe8992c2005-05-16 19:00:52 +0200266 return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 (val < 0 ? val - 500 : val + 500) / 1000 + 50];
268}
269
270/* for 8-bit temperature hyst and over registers */
Jean Delvare088341b2005-09-10 23:00:46 +0200271#define TEMP_FROM_REG(val) ((long)tempLUT[val] * 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273/* for 10-bit temperature readings */
274static inline long TEMP_FROM_REG10(u16 val)
275{
276 u16 eightBits = val >> 2;
277 u16 twoBits = val & 3;
278
279 /* no interpolation for these */
280 if (twoBits == 0 || eightBits == 255)
281 return TEMP_FROM_REG(eightBits);
282
283 /* do some linear interpolation */
284 return (tempLUT[eightBits] * (4 - twoBits) +
Jean Delvarebe8992c2005-05-16 19:00:52 +0200285 tempLUT[eightBits + 1] * twoBits) * 25;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286}
287
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288#define DIV_FROM_REG(val) (1 << (val))
289#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
290
Jean Delvareed6bafb2007-02-14 21:15:03 +0100291/* For each registered chip, we need to keep some data in memory.
292 The structure is dynamically allocated. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293struct via686a_data {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400294 unsigned short addr;
295 const char *name;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400296 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100297 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 char valid; /* !=0 if following fields are valid */
299 unsigned long last_updated; /* In jiffies */
300
301 u8 in[5]; /* Register value */
302 u8 in_max[5]; /* Register value */
303 u8 in_min[5]; /* Register value */
304 u8 fan[2]; /* Register value */
305 u8 fan_min[2]; /* Register value */
306 u16 temp[3]; /* Register value 10 bit */
307 u8 temp_over[3]; /* Register value */
308 u8 temp_hyst[3]; /* Register value */
309 u8 fan_div[2]; /* Register encoding, shifted right */
310 u16 alarms; /* Register encoding, combined */
311};
312
313static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
314
Jean Delvare2ec342e2007-06-09 10:11:16 -0400315static int via686a_probe(struct platform_device *pdev);
316static int via686a_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Jean Delvare2ec342e2007-06-09 10:11:16 -0400318static inline int via686a_read_value(struct via686a_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400320 return inb_p(data->addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
Jean Delvare2ec342e2007-06-09 10:11:16 -0400323static inline void via686a_write_value(struct via686a_data *data, u8 reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 u8 value)
325{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400326 outb_p(value, data->addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
329static struct via686a_data *via686a_update_device(struct device *dev);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400330static void via686a_init_device(struct via686a_data *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332/* following are the sysfs callback functions */
333
334/* 7 voltage sensors */
335static ssize_t show_in(struct device *dev, char *buf, int nr) {
336 struct via686a_data *data = via686a_update_device(dev);
337 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
338}
339
340static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
341 struct via686a_data *data = via686a_update_device(dev);
342 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
343}
344
345static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
346 struct via686a_data *data = via686a_update_device(dev);
347 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
348}
349
Jean Delvarebe8992c2005-05-16 19:00:52 +0200350static ssize_t set_in_min(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400352 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 unsigned long val = simple_strtoul(buf, NULL, 10);
354
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100355 mutex_lock(&data->update_lock);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200356 data->in_min[nr] = IN_TO_REG(val, nr);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400357 via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100359 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 return count;
361}
Jean Delvarebe8992c2005-05-16 19:00:52 +0200362static ssize_t set_in_max(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400364 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 unsigned long val = simple_strtoul(buf, NULL, 10);
366
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100367 mutex_lock(&data->update_lock);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200368 data->in_max[nr] = IN_TO_REG(val, nr);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400369 via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 data->in_max[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#define show_in_offset(offset) \
375static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400376 show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{ \
378 return show_in(dev, buf, offset); \
379} \
380static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400381 show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{ \
383 return show_in_min(dev, buf, offset); \
384} \
385static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400386 show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387{ \
388 return show_in_max(dev, buf, offset); \
389} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400390static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 const char *buf, size_t count) \
392{ \
393 return set_in_min(dev, buf, count, offset); \
394} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400395static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 const char *buf, size_t count) \
397{ \
398 return set_in_max(dev, buf, count, offset); \
399} \
400static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\
401static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
402 show_in##offset##_min, set_in##offset##_min); \
403static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
404 show_in##offset##_max, set_in##offset##_max);
405
406show_in_offset(0);
407show_in_offset(1);
408show_in_offset(2);
409show_in_offset(3);
410show_in_offset(4);
411
412/* 3 temperatures */
413static ssize_t show_temp(struct device *dev, char *buf, int nr) {
414 struct via686a_data *data = via686a_update_device(dev);
415 return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
416}
417static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
418 struct via686a_data *data = via686a_update_device(dev);
419 return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
420}
421static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
422 struct via686a_data *data = via686a_update_device(dev);
423 return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
424}
Jean Delvarebe8992c2005-05-16 19:00:52 +0200425static ssize_t set_temp_over(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400427 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 int val = simple_strtol(buf, NULL, 10);
429
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100430 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 data->temp_over[nr] = TEMP_TO_REG(val);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400432 via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
Jean Delvare563db2f2005-05-17 22:38:57 +0200433 data->temp_over[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100434 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 return count;
436}
Jean Delvarebe8992c2005-05-16 19:00:52 +0200437static ssize_t set_temp_hyst(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400439 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 int val = simple_strtol(buf, NULL, 10);
441
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100442 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 data->temp_hyst[nr] = TEMP_TO_REG(val);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400444 via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
Jean Delvare563db2f2005-05-17 22:38:57 +0200445 data->temp_hyst[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100446 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 return count;
448}
449#define show_temp_offset(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400450static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{ \
452 return show_temp(dev, buf, offset - 1); \
453} \
454static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400455show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{ \
457 return show_temp_over(dev, buf, offset - 1); \
458} \
459static ssize_t \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400460show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{ \
462 return show_temp_hyst(dev, buf, offset - 1); \
463} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400464static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 const char *buf, size_t count) \
466{ \
467 return set_temp_over(dev, buf, count, offset - 1); \
468} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400469static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 const char *buf, size_t count) \
471{ \
472 return set_temp_hyst(dev, buf, count, offset - 1); \
473} \
474static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\
475static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
476 show_temp_##offset##_over, set_temp_##offset##_over); \
477static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
Jean Delvarebe8992c2005-05-16 19:00:52 +0200478 show_temp_##offset##_hyst, set_temp_##offset##_hyst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480show_temp_offset(1);
481show_temp_offset(2);
482show_temp_offset(3);
483
484/* 2 Fans */
485static ssize_t show_fan(struct device *dev, char *buf, int nr) {
486 struct via686a_data *data = via686a_update_device(dev);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200487 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 DIV_FROM_REG(data->fan_div[nr])) );
489}
490static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
491 struct via686a_data *data = via686a_update_device(dev);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200492 return sprintf(buf, "%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
494}
495static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
496 struct via686a_data *data = via686a_update_device(dev);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200497 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
Jean Delvarebe8992c2005-05-16 19:00:52 +0200499static ssize_t set_fan_min(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400501 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 int val = simple_strtol(buf, NULL, 10);
503
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100504 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvare2ec342e2007-06-09 10:11:16 -0400506 via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100507 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 return count;
509}
Jean Delvarebe8992c2005-05-16 19:00:52 +0200510static ssize_t set_fan_div(struct device *dev, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 size_t count, int nr) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400512 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 int val = simple_strtol(buf, NULL, 10);
514 int old;
515
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100516 mutex_lock(&data->update_lock);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400517 old = via686a_read_value(data, VIA686A_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 data->fan_div[nr] = DIV_TO_REG(val);
519 old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400520 via686a_write_value(data, VIA686A_REG_FANDIV, old);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100521 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 return count;
523}
524
525#define show_fan_offset(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400526static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{ \
528 return show_fan(dev, buf, offset - 1); \
529} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400530static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{ \
532 return show_fan_min(dev, buf, offset - 1); \
533} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400534static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{ \
536 return show_fan_div(dev, buf, offset - 1); \
537} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400538static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 const char *buf, size_t count) \
540{ \
541 return set_fan_min(dev, buf, count, offset - 1); \
542} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400543static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 const char *buf, size_t count) \
545{ \
546 return set_fan_div(dev, buf, count, offset - 1); \
547} \
548static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
549static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
550 show_fan_##offset##_min, set_fan_##offset##_min); \
551static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
552 show_fan_##offset##_div, set_fan_##offset##_div);
553
554show_fan_offset(1);
555show_fan_offset(2);
556
557/* Alarms */
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400558static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare68188ba2005-05-16 18:52:38 +0200560 return sprintf(buf, "%u\n", data->alarms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561}
Jean Delvare1d66c642005-04-18 21:16:59 -0700562static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Jean Delvare2ec342e2007-06-09 10:11:16 -0400564static ssize_t show_name(struct device *dev, struct device_attribute
565 *devattr, char *buf)
566{
567 struct via686a_data *data = dev_get_drvdata(dev);
568 return sprintf(buf, "%s\n", data->name);
569}
570static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
571
Jean Delvarea5ebe662006-09-24 21:24:46 +0200572static struct attribute *via686a_attributes[] = {
573 &dev_attr_in0_input.attr,
574 &dev_attr_in1_input.attr,
575 &dev_attr_in2_input.attr,
576 &dev_attr_in3_input.attr,
577 &dev_attr_in4_input.attr,
578 &dev_attr_in0_min.attr,
579 &dev_attr_in1_min.attr,
580 &dev_attr_in2_min.attr,
581 &dev_attr_in3_min.attr,
582 &dev_attr_in4_min.attr,
583 &dev_attr_in0_max.attr,
584 &dev_attr_in1_max.attr,
585 &dev_attr_in2_max.attr,
586 &dev_attr_in3_max.attr,
587 &dev_attr_in4_max.attr,
588
589 &dev_attr_temp1_input.attr,
590 &dev_attr_temp2_input.attr,
591 &dev_attr_temp3_input.attr,
592 &dev_attr_temp1_max.attr,
593 &dev_attr_temp2_max.attr,
594 &dev_attr_temp3_max.attr,
595 &dev_attr_temp1_max_hyst.attr,
596 &dev_attr_temp2_max_hyst.attr,
597 &dev_attr_temp3_max_hyst.attr,
598
599 &dev_attr_fan1_input.attr,
600 &dev_attr_fan2_input.attr,
601 &dev_attr_fan1_min.attr,
602 &dev_attr_fan2_min.attr,
603 &dev_attr_fan1_div.attr,
604 &dev_attr_fan2_div.attr,
605
606 &dev_attr_alarms.attr,
Jean Delvare2ec342e2007-06-09 10:11:16 -0400607 &dev_attr_name.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200608 NULL
609};
610
611static const struct attribute_group via686a_group = {
612 .attrs = via686a_attributes,
613};
614
Jean Delvare2ec342e2007-06-09 10:11:16 -0400615static struct platform_driver via686a_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100616 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200617 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100618 .name = "via686a",
619 },
Jean Delvare2ec342e2007-06-09 10:11:16 -0400620 .probe = via686a_probe,
621 .remove = __devexit_p(via686a_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622};
623
624
625/* This is called when the module is loaded */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400626static int __devinit via686a_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 struct via686a_data *data;
Jean Delvare2ec342e2007-06-09 10:11:16 -0400629 struct resource *res;
630 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
632 /* Reserve the ISA region */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400633 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
634 if (!request_region(res->start, VIA686A_EXTENT,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100635 via686a_driver.driver.name)) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400636 dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
637 (unsigned long)res->start, (unsigned long)res->end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 return -ENODEV;
639 }
640
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200641 if (!(data = kzalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 err = -ENOMEM;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400643 goto exit_release;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Jean Delvare2ec342e2007-06-09 10:11:16 -0400646 platform_set_drvdata(pdev, data);
647 data->addr = res->start;
648 data->name = "via686a";
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100649 mutex_init(&data->update_lock);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200650
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 /* Initialize the VIA686A chip */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400652 via686a_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
654 /* Register sysfs hooks */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400655 if ((err = sysfs_create_group(&pdev->dev.kobj, &via686a_group)))
656 goto exit_free;
Jean Delvarea5ebe662006-09-24 21:24:46 +0200657
Jean Delvare2ec342e2007-06-09 10:11:16 -0400658 data->class_dev = hwmon_device_register(&pdev->dev);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400659 if (IS_ERR(data->class_dev)) {
660 err = PTR_ERR(data->class_dev);
Jean Delvarea5ebe662006-09-24 21:24:46 +0200661 goto exit_remove_files;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400662 }
663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 return 0;
665
Jean Delvarea5ebe662006-09-24 21:24:46 +0200666exit_remove_files:
Jean Delvare2ec342e2007-06-09 10:11:16 -0400667 sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400668exit_free:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 kfree(data);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400670exit_release:
Jean Delvare2ec342e2007-06-09 10:11:16 -0400671 release_region(res->start, VIA686A_EXTENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 return err;
673}
674
Jean Delvare2ec342e2007-06-09 10:11:16 -0400675static int __devexit via686a_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400677 struct via686a_data *data = platform_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400679 hwmon_device_unregister(data->class_dev);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400680 sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400681
Jean Delvare2ec342e2007-06-09 10:11:16 -0400682 release_region(data->addr, VIA686A_EXTENT);
683 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400684 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
686 return 0;
687}
688
Jean Delvare2ec342e2007-06-09 10:11:16 -0400689static void __devinit via686a_init_device(struct via686a_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690{
691 u8 reg;
692
693 /* Start monitoring */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400694 reg = via686a_read_value(data, VIA686A_REG_CONFIG);
695 via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
697 /* Configure temp interrupt mode for continuous-interrupt operation */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400698 reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE);
699 via686a_write_value(data, VIA686A_REG_TEMP_MODE,
Jean Delvare58fe0802007-06-09 10:11:16 -0400700 (reg & ~VIA686A_TEMP_MODE_MASK)
701 | VIA686A_TEMP_MODE_CONTINUOUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702}
703
704static struct via686a_data *via686a_update_device(struct device *dev)
705{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400706 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 int i;
708
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100709 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
712 || !data->valid) {
713 for (i = 0; i <= 4; i++) {
714 data->in[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400715 via686a_read_value(data, VIA686A_REG_IN(i));
716 data->in_min[i] = via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 VIA686A_REG_IN_MIN
718 (i));
719 data->in_max[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400720 via686a_read_value(data, VIA686A_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 }
722 for (i = 1; i <= 2; i++) {
723 data->fan[i - 1] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400724 via686a_read_value(data, VIA686A_REG_FAN(i));
725 data->fan_min[i - 1] = via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 VIA686A_REG_FAN_MIN(i));
727 }
728 for (i = 0; i <= 2; i++) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400729 data->temp[i] = via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200730 VIA686A_REG_TEMP[i]) << 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 data->temp_over[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400732 via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200733 VIA686A_REG_TEMP_OVER[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 data->temp_hyst[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400735 via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200736 VIA686A_REG_TEMP_HYST[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 }
Jean Delvarebe8992c2005-05-16 19:00:52 +0200738 /* add in lower 2 bits
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
740 temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
741 temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
742 */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400743 data->temp[0] |= (via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 VIA686A_REG_TEMP_LOW1)
745 & 0xc0) >> 6;
746 data->temp[1] |=
Jean Delvare2ec342e2007-06-09 10:11:16 -0400747 (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 0x30) >> 4;
749 data->temp[2] |=
Jean Delvare2ec342e2007-06-09 10:11:16 -0400750 (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 0xc0) >> 6;
752
Jean Delvare2ec342e2007-06-09 10:11:16 -0400753 i = via686a_read_value(data, VIA686A_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 data->fan_div[0] = (i >> 4) & 0x03;
755 data->fan_div[1] = i >> 6;
756 data->alarms =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400757 via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 VIA686A_REG_ALARM1) |
Jean Delvare2ec342e2007-06-09 10:11:16 -0400759 (via686a_read_value(data, VIA686A_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 data->last_updated = jiffies;
761 data->valid = 1;
762 }
763
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100764 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 return data;
767}
768
769static struct pci_device_id via686a_pci_ids[] = {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200770 { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
771 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772};
773
774MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
775
Jean Delvare2ec342e2007-06-09 10:11:16 -0400776static int __devinit via686a_device_add(unsigned short address)
777{
778 struct resource res = {
779 .start = address,
780 .end = address + VIA686A_EXTENT - 1,
781 .name = "via686a",
782 .flags = IORESOURCE_IO,
783 };
784 int err;
785
786 pdev = platform_device_alloc("via686a", address);
787 if (!pdev) {
788 err = -ENOMEM;
789 printk(KERN_ERR "via686a: Device allocation failed\n");
790 goto exit;
791 }
792
793 err = platform_device_add_resources(pdev, &res, 1);
794 if (err) {
795 printk(KERN_ERR "via686a: Device resource addition failed "
796 "(%d)\n", err);
797 goto exit_device_put;
798 }
799
800 err = platform_device_add(pdev);
801 if (err) {
802 printk(KERN_ERR "via686a: Device addition failed (%d)\n",
803 err);
804 goto exit_device_put;
805 }
806
807 return 0;
808
809exit_device_put:
810 platform_device_put(pdev);
811exit:
812 return err;
813}
814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815static int __devinit via686a_pci_probe(struct pci_dev *dev,
Jean Delvarebe8992c2005-05-16 19:00:52 +0200816 const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400818 u16 address, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Jean Delvare2ec342e2007-06-09 10:11:16 -0400820 if (force_addr) {
821 address = force_addr & ~(VIA686A_EXTENT - 1);
822 dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
823 if (PCIBIOS_SUCCESSFUL !=
824 pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
825 return -ENODEV;
826 }
Jean Delvarebe8992c2005-05-16 19:00:52 +0200827 if (PCIBIOS_SUCCESSFUL !=
828 pci_read_config_word(dev, VIA686A_BASE_REG, &val))
829 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Jean Delvare2d8672c2005-07-19 23:56:35 +0200831 address = val & ~(VIA686A_EXTENT - 1);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400832 if (address == 0) {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200833 dev_err(&dev->dev, "base address not set - upgrade BIOS "
834 "or use force_addr=0xaddr\n");
835 return -ENODEV;
836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Jean Delvare2ec342e2007-06-09 10:11:16 -0400838 if (PCIBIOS_SUCCESSFUL !=
839 pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
840 return -ENODEV;
841 if (!(val & 0x0001)) {
842 if (!force_addr) {
843 dev_warn(&dev->dev, "Sensors disabled, enable "
844 "with force_addr=0x%x\n", address);
845 return -ENODEV;
846 }
847
848 dev_warn(&dev->dev, "Enabling sensors\n");
849 if (PCIBIOS_SUCCESSFUL !=
850 pci_write_config_word(dev, VIA686A_ENABLE_REG,
851 val | 0x0001))
852 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 }
854
Jean Delvare2ec342e2007-06-09 10:11:16 -0400855 if (platform_driver_register(&via686a_driver))
856 goto exit;
857
858 /* Sets global pdev as a side effect */
859 if (via686a_device_add(address))
860 goto exit_unregister;
861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 /* Always return failure here. This is to allow other drivers to bind
863 * to this pci device. We don't really want to have control over the
864 * pci device, we only wanted to read as few register values from it.
865 */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400866 s_bridge = pci_dev_get(dev);
867 return -ENODEV;
868
869exit_unregister:
870 platform_driver_unregister(&via686a_driver);
871exit:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 return -ENODEV;
873}
874
875static struct pci_driver via686a_pci_driver = {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200876 .name = "via686a",
877 .id_table = via686a_pci_ids,
878 .probe = via686a_pci_probe,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879};
880
881static int __init sm_via686a_init(void)
882{
Jean Delvarebe8992c2005-05-16 19:00:52 +0200883 return pci_register_driver(&via686a_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884}
885
886static void __exit sm_via686a_exit(void)
887{
888 pci_unregister_driver(&via686a_pci_driver);
889 if (s_bridge != NULL) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400890 platform_device_unregister(pdev);
891 platform_driver_unregister(&via686a_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 pci_dev_put(s_bridge);
893 s_bridge = NULL;
894 }
895}
896
897MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
Jean Delvarebe8992c2005-05-16 19:00:52 +0200898 "Mark Studebaker <mdsxyz123@yahoo.com> "
899 "and Bob Dougherty <bobd@stanford.edu>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900MODULE_DESCRIPTION("VIA 686A Sensor device");
901MODULE_LICENSE("GPL");
902
903module_init(sm_via686a_init);
904module_exit(sm_via686a_exit);