blob: f1ee5e731968d7e5edd0c2e3b0ab1cdd521bd2ba [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>,
Jan Engelhardt96de0e22007-10-19 23:21:04 +02006 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>
Jean Delvare1e71a5a2007-06-09 10:11:16 -040039#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040040#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010042#include <linux/mutex.h>
Jean Delvarea5ebe662006-09-24 21:24:46 +020043#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <asm/io.h>
45
46
47/* If force_addr is set to anything different from 0, we forcibly enable
48 the device at the given address. */
Jean Delvare02002962005-09-25 16:26:44 +020049static unsigned short force_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050module_param(force_addr, ushort, 0);
51MODULE_PARM_DESC(force_addr,
52 "Initialize the base address of the sensors");
53
Jean Delvare2ec342e2007-06-09 10:11:16 -040054static struct platform_device *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
56/*
57 The Via 686a southbridge has a LM78-like chip integrated on the same IC.
58 This driver is a customized copy of lm78.c
59*/
60
61/* Many VIA686A constants specified below */
62
63/* Length of ISA address segment */
Jean Delvarebe8992c2005-05-16 19:00:52 +020064#define VIA686A_EXTENT 0x80
65#define VIA686A_BASE_REG 0x70
66#define VIA686A_ENABLE_REG 0x74
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68/* The VIA686A registers */
69/* ins numbered 0-4 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020070#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
71#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
72#define VIA686A_REG_IN(nr) (0x22 + (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74/* fans numbered 1-2 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020075#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
76#define VIA686A_REG_FAN(nr) (0x28 + (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/* temps numbered 1-3 */
Jean Delvare563db2f2005-05-17 22:38:57 +020079static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f };
80static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d };
81static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e };
Jean Delvarebe8992c2005-05-16 19:00:52 +020082/* bits 7-6 */
83#define VIA686A_REG_TEMP_LOW1 0x4b
84/* 2 = bits 5-4, 3 = bits 7-6 */
85#define VIA686A_REG_TEMP_LOW23 0x49
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Jean Delvarebe8992c2005-05-16 19:00:52 +020087#define VIA686A_REG_ALARM1 0x41
88#define VIA686A_REG_ALARM2 0x42
89#define VIA686A_REG_FANDIV 0x47
90#define VIA686A_REG_CONFIG 0x40
91/* The following register sets temp interrupt mode (bits 1-0 for temp1,
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 3-2 for temp2, 5-4 for temp3). Modes are:
93 00 interrupt stays as long as value is out-of-range
94 01 interrupt is cleared once register is read (default)
95 10 comparator mode- like 00, but ignores hysteresis
96 11 same as 00 */
Jean Delvarebe8992c2005-05-16 19:00:52 +020097#define VIA686A_REG_TEMP_MODE 0x4b
Linus Torvalds1da177e2005-04-16 15:20:36 -070098/* We'll just assume that you want to set all 3 simultaneously: */
Jean Delvarebe8992c2005-05-16 19:00:52 +020099#define VIA686A_TEMP_MODE_MASK 0x3F
100#define VIA686A_TEMP_MODE_CONTINUOUS 0x00
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102/* Conversions. Limit checking is only done on the TO_REG
Jean Delvarebe8992c2005-05-16 19:00:52 +0200103 variants.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105********* VOLTAGE CONVERSIONS (Bob Dougherty) ********
106 From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
107 voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp
108 voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V
109 voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V
110 voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V
111 voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V
112 in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
113 That is:
114 volts = (25*regVal+133)*factor
115 regVal = (volts/factor-133)/25
Jean Delvarebe8992c2005-05-16 19:00:52 +0200116 (These conversions were contributed by Jonathan Teh Soon Yew
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 <j.teh@iname.com>) */
118static inline u8 IN_TO_REG(long val, int inNum)
119{
120 /* To avoid floating point, we multiply constants by 10 (100 for +12V).
121 Rounding is done (120500 is actually 133000 - 12500).
122 Remember that val is expressed in 0.001V/bit, which is why we divide
123 by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
124 for the constants. */
125 if (inNum <= 1)
126 return (u8)
127 SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255);
128 else if (inNum == 2)
129 return (u8)
130 SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255);
131 else if (inNum == 3)
132 return (u8)
133 SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255);
134 else
135 return (u8)
136 SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255);
137}
138
139static inline long IN_FROM_REG(u8 val, int inNum)
140{
141 /* To avoid floating point, we multiply constants by 10 (100 for +12V).
142 We also multiply them by 1000 because we want 0.001V/bit for the
143 output value. Rounding is done. */
144 if (inNum <= 1)
145 return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
146 else if (inNum == 2)
147 return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
148 else if (inNum == 3)
149 return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
150 else
151 return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
152}
153
154/********* FAN RPM CONVERSIONS ********/
155/* Higher register values = slower fans (the fan's strobe gates a counter).
156 But this chip saturates back at 0, not at 255 like all the other chips.
157 So, 0 means 0 RPM */
158static inline u8 FAN_TO_REG(long rpm, int div)
159{
160 if (rpm == 0)
161 return 0;
162 rpm = SENSORS_LIMIT(rpm, 1, 1000000);
163 return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
164}
165
166#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
167
168/******** TEMP CONVERSIONS (Bob Dougherty) *********/
169/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
170 if(temp<169)
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300171 return double(temp)*0.427-32.08;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 else if(temp>=169 && temp<=202)
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300173 return double(temp)*0.582-58.16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 else
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300175 return double(temp)*0.924-127.33;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Jean Delvarebe8992c2005-05-16 19:00:52 +0200177 A fifth-order polynomial fits the unofficial data (provided by Alex van
178 Kaam <darkside@chello.nl>) a bit better. It also give more reasonable
179 numbers on my machine (ie. they agree with what my BIOS tells me).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 Here's the fifth-order fit to the 8-bit data:
Jean Delvarebe8992c2005-05-16 19:00:52 +0200181 temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
Denis Vlasenko6328c0e2005-06-22 10:25:13 +0300182 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Jean Delvarebe8992c2005-05-16 19:00:52 +0200184 (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 finding my typos in this formula!)
186
Jean Delvarebe8992c2005-05-16 19:00:52 +0200187 Alas, none of the elegant function-fit solutions will work because we
188 aren't allowed to use floating point in the kernel and doing it with
189 integers doesn't provide enough precision. So we'll do boring old
190 look-up table stuff. The unofficial data (see below) have effectively
191 7-bit resolution (they are rounded to the nearest degree). I'm assuming
192 that the transfer function of the device is monotonic and smooth, so a
193 smooth function fit to the data will allow us to get better precision.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 I used the 5th-order poly fit described above and solved for
Jean Delvarebe8992c2005-05-16 19:00:52 +0200195 VIA register values 0-255. I *10 before rounding, so we get tenth-degree
196 precision. (I could have done all 1024 values for our 10-bit readings,
197 but the function is very linear in the useful range (0-80 deg C), so
198 we'll just use linear interpolation for 10-bit readings.) So, tempLUT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 is the temp at via register values 0-255: */
Jean Delvare088341b2005-09-10 23:00:46 +0200200static const s16 tempLUT[] =
Jean Delvarebe8992c2005-05-16 19:00:52 +0200201{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
202 -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
203 -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
204 -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
205 -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
206 -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
207 -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
208 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
209 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
210 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
211 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
212 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
213 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
214 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
215 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
216 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
217 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
218 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
219 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
220 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
221 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
222 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223};
224
Jean Delvarebe8992c2005-05-16 19:00:52 +0200225/* the original LUT values from Alex van Kaam <darkside@chello.nl>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 (for via register values 12-240):
227{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
228-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
229-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
230-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,
23112,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
23222,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
23333,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
23445,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
23561,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
23685,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
237
238
239 Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed
Jean Delvarebe8992c2005-05-16 19:00:52 +0200240 an extra term for a good fit to these inverse data!) and then
241 solving for each temp value from -50 to 110 (the useable range for
242 this chip). Here's the fit:
243 viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
245 Note that n=161: */
246static const u8 viaLUT[] =
Jean Delvarebe8992c2005-05-16 19:00:52 +0200247{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
248 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
249 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
250 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
251 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
252 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
253 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
254 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
255 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
256 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
257 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
258 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
259 239, 240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
262/* Converting temps to (8-bit) hyst and over registers
263 No interpolation here.
264 The +50 is because the temps start at -50 */
265static inline u8 TEMP_TO_REG(long val)
266{
Jean Delvarebe8992c2005-05-16 19:00:52 +0200267 return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 (val < 0 ? val - 500 : val + 500) / 1000 + 50];
269}
270
271/* for 8-bit temperature hyst and over registers */
Jean Delvare088341b2005-09-10 23:00:46 +0200272#define TEMP_FROM_REG(val) ((long)tempLUT[val] * 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
274/* for 10-bit temperature readings */
275static inline long TEMP_FROM_REG10(u16 val)
276{
277 u16 eightBits = val >> 2;
278 u16 twoBits = val & 3;
279
280 /* no interpolation for these */
281 if (twoBits == 0 || eightBits == 255)
282 return TEMP_FROM_REG(eightBits);
283
284 /* do some linear interpolation */
285 return (tempLUT[eightBits] * (4 - twoBits) +
Jean Delvarebe8992c2005-05-16 19:00:52 +0200286 tempLUT[eightBits + 1] * twoBits) * 25;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287}
288
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289#define DIV_FROM_REG(val) (1 << (val))
290#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
291
Jean Delvareed6bafb2007-02-14 21:15:03 +0100292/* For each registered chip, we need to keep some data in memory.
293 The structure is dynamically allocated. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294struct via686a_data {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400295 unsigned short addr;
296 const char *name;
Tony Jones1beeffe2007-08-20 13:46:20 -0700297 struct device *hwmon_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100298 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 char valid; /* !=0 if following fields are valid */
300 unsigned long last_updated; /* In jiffies */
301
302 u8 in[5]; /* Register value */
303 u8 in_max[5]; /* Register value */
304 u8 in_min[5]; /* Register value */
305 u8 fan[2]; /* Register value */
306 u8 fan_min[2]; /* Register value */
307 u16 temp[3]; /* Register value 10 bit */
308 u8 temp_over[3]; /* Register value */
309 u8 temp_hyst[3]; /* Register value */
310 u8 fan_div[2]; /* Register encoding, shifted right */
311 u16 alarms; /* Register encoding, combined */
312};
313
314static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
315
Jean Delvare2ec342e2007-06-09 10:11:16 -0400316static int via686a_probe(struct platform_device *pdev);
Jean Delvared0546122007-07-22 12:09:48 +0200317static int __devexit via686a_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
Jean Delvare2ec342e2007-06-09 10:11:16 -0400319static inline int via686a_read_value(struct via686a_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400321 return inb_p(data->addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
Jean Delvare2ec342e2007-06-09 10:11:16 -0400324static inline void via686a_write_value(struct via686a_data *data, u8 reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 u8 value)
326{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400327 outb_p(value, data->addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
330static struct via686a_data *via686a_update_device(struct device *dev);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400331static void via686a_init_device(struct via686a_data *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333/* following are the sysfs callback functions */
334
335/* 7 voltage sensors */
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400336static ssize_t show_in(struct device *dev, struct device_attribute *da,
337 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400339 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
340 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
342}
343
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400344static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
345 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400347 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
348 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
350}
351
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400352static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
353 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400355 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
356 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
358}
359
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400360static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
361 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400362 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400363 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
364 int nr = attr->index;
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_min[nr] = IN_TO_REG(val, nr);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400369 via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 data->in_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}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400374static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
375 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400376 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400377 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
378 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 unsigned long val = simple_strtoul(buf, NULL, 10);
380
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100381 mutex_lock(&data->update_lock);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200382 data->in_max[nr] = IN_TO_REG(val, nr);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400383 via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100385 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 return count;
387}
388#define show_in_offset(offset) \
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400389static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
390 show_in, NULL, offset); \
391static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
392 show_in_min, set_in_min, offset); \
393static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
394 show_in_max, set_in_max, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
396show_in_offset(0);
397show_in_offset(1);
398show_in_offset(2);
399show_in_offset(3);
400show_in_offset(4);
401
402/* 3 temperatures */
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400403static ssize_t show_temp(struct device *dev, struct device_attribute *da,
404 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400406 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
407 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
409}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400410static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
411 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400413 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
414 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
416}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400417static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
418 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400420 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
421 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
423}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400424static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
425 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400426 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400427 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
428 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 int val = simple_strtol(buf, NULL, 10);
430
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100431 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 data->temp_over[nr] = TEMP_TO_REG(val);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400433 via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
Jean Delvare563db2f2005-05-17 22:38:57 +0200434 data->temp_over[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100435 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 return count;
437}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400438static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
439 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400440 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400441 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
442 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 int val = simple_strtol(buf, NULL, 10);
444
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100445 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 data->temp_hyst[nr] = TEMP_TO_REG(val);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400447 via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
Jean Delvare563db2f2005-05-17 22:38:57 +0200448 data->temp_hyst[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100449 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 return count;
451}
452#define show_temp_offset(offset) \
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400453static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
454 show_temp, NULL, offset - 1); \
455static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
456 show_temp_over, set_temp_over, offset - 1); \
457static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
458 show_temp_hyst, set_temp_hyst, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
460show_temp_offset(1);
461show_temp_offset(2);
462show_temp_offset(3);
463
464/* 2 Fans */
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400465static ssize_t show_fan(struct device *dev, struct device_attribute *da,
466 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400468 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
469 int nr = attr->index;
Jean Delvarebe8992c2005-05-16 19:00:52 +0200470 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 DIV_FROM_REG(data->fan_div[nr])) );
472}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400473static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
474 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400476 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
477 int nr = attr->index;
Jean Delvarebe8992c2005-05-16 19:00:52 +0200478 return sprintf(buf, "%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
480}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400481static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
482 char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400484 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
485 int nr = attr->index;
Jean Delvarebe8992c2005-05-16 19:00:52 +0200486 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400488static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
489 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400490 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400491 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
492 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 int val = simple_strtol(buf, NULL, 10);
494
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100495 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvare2ec342e2007-06-09 10:11:16 -0400497 via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100498 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 return count;
500}
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400501static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
502 const char *buf, size_t count) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400503 struct via686a_data *data = dev_get_drvdata(dev);
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400504 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
505 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 int val = simple_strtol(buf, NULL, 10);
507 int old;
508
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100509 mutex_lock(&data->update_lock);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400510 old = via686a_read_value(data, VIA686A_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 data->fan_div[nr] = DIV_TO_REG(val);
512 old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400513 via686a_write_value(data, VIA686A_REG_FANDIV, old);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100514 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 return count;
516}
517
518#define show_fan_offset(offset) \
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400519static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
520 show_fan, NULL, offset - 1); \
521static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
522 show_fan_min, set_fan_min, offset - 1); \
523static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
524 show_fan_div, set_fan_div, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
526show_fan_offset(1);
527show_fan_offset(2);
528
529/* Alarms */
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400530static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 struct via686a_data *data = via686a_update_device(dev);
Jean Delvare68188ba2005-05-16 18:52:38 +0200532 return sprintf(buf, "%u\n", data->alarms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533}
Jean Delvare1d66c642005-04-18 21:16:59 -0700534static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Jean Delvare13ff05e2008-01-06 15:42:04 +0100536static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
537 char *buf)
538{
539 int bitnr = to_sensor_dev_attr(attr)->index;
540 struct via686a_data *data = via686a_update_device(dev);
541 return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
542}
543static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
544static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
545static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
546static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
547static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
548static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
549static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 11);
550static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 15);
551static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
552static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
553
Jean Delvare2ec342e2007-06-09 10:11:16 -0400554static ssize_t show_name(struct device *dev, struct device_attribute
555 *devattr, char *buf)
556{
557 struct via686a_data *data = dev_get_drvdata(dev);
558 return sprintf(buf, "%s\n", data->name);
559}
560static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
561
Jean Delvarea5ebe662006-09-24 21:24:46 +0200562static struct attribute *via686a_attributes[] = {
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400563 &sensor_dev_attr_in0_input.dev_attr.attr,
564 &sensor_dev_attr_in1_input.dev_attr.attr,
565 &sensor_dev_attr_in2_input.dev_attr.attr,
566 &sensor_dev_attr_in3_input.dev_attr.attr,
567 &sensor_dev_attr_in4_input.dev_attr.attr,
568 &sensor_dev_attr_in0_min.dev_attr.attr,
569 &sensor_dev_attr_in1_min.dev_attr.attr,
570 &sensor_dev_attr_in2_min.dev_attr.attr,
571 &sensor_dev_attr_in3_min.dev_attr.attr,
572 &sensor_dev_attr_in4_min.dev_attr.attr,
573 &sensor_dev_attr_in0_max.dev_attr.attr,
574 &sensor_dev_attr_in1_max.dev_attr.attr,
575 &sensor_dev_attr_in2_max.dev_attr.attr,
576 &sensor_dev_attr_in3_max.dev_attr.attr,
577 &sensor_dev_attr_in4_max.dev_attr.attr,
Jean Delvare13ff05e2008-01-06 15:42:04 +0100578 &sensor_dev_attr_in0_alarm.dev_attr.attr,
579 &sensor_dev_attr_in1_alarm.dev_attr.attr,
580 &sensor_dev_attr_in2_alarm.dev_attr.attr,
581 &sensor_dev_attr_in3_alarm.dev_attr.attr,
582 &sensor_dev_attr_in4_alarm.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200583
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400584 &sensor_dev_attr_temp1_input.dev_attr.attr,
585 &sensor_dev_attr_temp2_input.dev_attr.attr,
586 &sensor_dev_attr_temp3_input.dev_attr.attr,
587 &sensor_dev_attr_temp1_max.dev_attr.attr,
588 &sensor_dev_attr_temp2_max.dev_attr.attr,
589 &sensor_dev_attr_temp3_max.dev_attr.attr,
590 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
591 &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
592 &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
Jean Delvare13ff05e2008-01-06 15:42:04 +0100593 &sensor_dev_attr_temp1_alarm.dev_attr.attr,
594 &sensor_dev_attr_temp2_alarm.dev_attr.attr,
595 &sensor_dev_attr_temp3_alarm.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200596
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400597 &sensor_dev_attr_fan1_input.dev_attr.attr,
598 &sensor_dev_attr_fan2_input.dev_attr.attr,
599 &sensor_dev_attr_fan1_min.dev_attr.attr,
600 &sensor_dev_attr_fan2_min.dev_attr.attr,
601 &sensor_dev_attr_fan1_div.dev_attr.attr,
602 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare13ff05e2008-01-06 15:42:04 +0100603 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
604 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200605
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
Tony Jones1beeffe2007-08-20 13:46:20 -0700658 data->hwmon_dev = hwmon_device_register(&pdev->dev);
659 if (IS_ERR(data->hwmon_dev)) {
660 err = PTR_ERR(data->hwmon_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
Tony Jones1beeffe2007-08-20 13:46:20 -0700679 hwmon_device_unregister(data->hwmon_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
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200897MODULE_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);