blob: 24a6851491d00d7c9655d3a67ab43dd2d53c60e5 [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>
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;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400297 struct class_device *class_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);
317static int 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 Delvare2ec342e2007-06-09 10:11:16 -0400536static ssize_t show_name(struct device *dev, struct device_attribute
537 *devattr, char *buf)
538{
539 struct via686a_data *data = dev_get_drvdata(dev);
540 return sprintf(buf, "%s\n", data->name);
541}
542static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
543
Jean Delvarea5ebe662006-09-24 21:24:46 +0200544static struct attribute *via686a_attributes[] = {
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400545 &sensor_dev_attr_in0_input.dev_attr.attr,
546 &sensor_dev_attr_in1_input.dev_attr.attr,
547 &sensor_dev_attr_in2_input.dev_attr.attr,
548 &sensor_dev_attr_in3_input.dev_attr.attr,
549 &sensor_dev_attr_in4_input.dev_attr.attr,
550 &sensor_dev_attr_in0_min.dev_attr.attr,
551 &sensor_dev_attr_in1_min.dev_attr.attr,
552 &sensor_dev_attr_in2_min.dev_attr.attr,
553 &sensor_dev_attr_in3_min.dev_attr.attr,
554 &sensor_dev_attr_in4_min.dev_attr.attr,
555 &sensor_dev_attr_in0_max.dev_attr.attr,
556 &sensor_dev_attr_in1_max.dev_attr.attr,
557 &sensor_dev_attr_in2_max.dev_attr.attr,
558 &sensor_dev_attr_in3_max.dev_attr.attr,
559 &sensor_dev_attr_in4_max.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200560
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400561 &sensor_dev_attr_temp1_input.dev_attr.attr,
562 &sensor_dev_attr_temp2_input.dev_attr.attr,
563 &sensor_dev_attr_temp3_input.dev_attr.attr,
564 &sensor_dev_attr_temp1_max.dev_attr.attr,
565 &sensor_dev_attr_temp2_max.dev_attr.attr,
566 &sensor_dev_attr_temp3_max.dev_attr.attr,
567 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
568 &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
569 &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200570
Jean Delvare1e71a5a2007-06-09 10:11:16 -0400571 &sensor_dev_attr_fan1_input.dev_attr.attr,
572 &sensor_dev_attr_fan2_input.dev_attr.attr,
573 &sensor_dev_attr_fan1_min.dev_attr.attr,
574 &sensor_dev_attr_fan2_min.dev_attr.attr,
575 &sensor_dev_attr_fan1_div.dev_attr.attr,
576 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200577
578 &dev_attr_alarms.attr,
Jean Delvare2ec342e2007-06-09 10:11:16 -0400579 &dev_attr_name.attr,
Jean Delvarea5ebe662006-09-24 21:24:46 +0200580 NULL
581};
582
583static const struct attribute_group via686a_group = {
584 .attrs = via686a_attributes,
585};
586
Jean Delvare2ec342e2007-06-09 10:11:16 -0400587static struct platform_driver via686a_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100588 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200589 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100590 .name = "via686a",
591 },
Jean Delvare2ec342e2007-06-09 10:11:16 -0400592 .probe = via686a_probe,
593 .remove = __devexit_p(via686a_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594};
595
596
597/* This is called when the module is loaded */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400598static int __devinit via686a_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 struct via686a_data *data;
Jean Delvare2ec342e2007-06-09 10:11:16 -0400601 struct resource *res;
602 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
604 /* Reserve the ISA region */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400605 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
606 if (!request_region(res->start, VIA686A_EXTENT,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100607 via686a_driver.driver.name)) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400608 dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
609 (unsigned long)res->start, (unsigned long)res->end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 return -ENODEV;
611 }
612
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200613 if (!(data = kzalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 err = -ENOMEM;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400615 goto exit_release;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
Jean Delvare2ec342e2007-06-09 10:11:16 -0400618 platform_set_drvdata(pdev, data);
619 data->addr = res->start;
620 data->name = "via686a";
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100621 mutex_init(&data->update_lock);
Jean Delvarebe8992c2005-05-16 19:00:52 +0200622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 /* Initialize the VIA686A chip */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400624 via686a_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 /* Register sysfs hooks */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400627 if ((err = sysfs_create_group(&pdev->dev.kobj, &via686a_group)))
628 goto exit_free;
Jean Delvarea5ebe662006-09-24 21:24:46 +0200629
Jean Delvare2ec342e2007-06-09 10:11:16 -0400630 data->class_dev = hwmon_device_register(&pdev->dev);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400631 if (IS_ERR(data->class_dev)) {
632 err = PTR_ERR(data->class_dev);
Jean Delvarea5ebe662006-09-24 21:24:46 +0200633 goto exit_remove_files;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400634 }
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 return 0;
637
Jean Delvarea5ebe662006-09-24 21:24:46 +0200638exit_remove_files:
Jean Delvare2ec342e2007-06-09 10:11:16 -0400639 sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400640exit_free:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 kfree(data);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400642exit_release:
Jean Delvare2ec342e2007-06-09 10:11:16 -0400643 release_region(res->start, VIA686A_EXTENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 return err;
645}
646
Jean Delvare2ec342e2007-06-09 10:11:16 -0400647static int __devexit via686a_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400649 struct via686a_data *data = platform_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400651 hwmon_device_unregister(data->class_dev);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400652 sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400653
Jean Delvare2ec342e2007-06-09 10:11:16 -0400654 release_region(data->addr, VIA686A_EXTENT);
655 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400656 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 return 0;
659}
660
Jean Delvare2ec342e2007-06-09 10:11:16 -0400661static void __devinit via686a_init_device(struct via686a_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 u8 reg;
664
665 /* Start monitoring */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400666 reg = via686a_read_value(data, VIA686A_REG_CONFIG);
667 via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 /* Configure temp interrupt mode for continuous-interrupt operation */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400670 reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE);
671 via686a_write_value(data, VIA686A_REG_TEMP_MODE,
Jean Delvare58fe0802007-06-09 10:11:16 -0400672 (reg & ~VIA686A_TEMP_MODE_MASK)
673 | VIA686A_TEMP_MODE_CONTINUOUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674}
675
676static struct via686a_data *via686a_update_device(struct device *dev)
677{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400678 struct via686a_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 int i;
680
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100681 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
684 || !data->valid) {
685 for (i = 0; i <= 4; i++) {
686 data->in[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400687 via686a_read_value(data, VIA686A_REG_IN(i));
688 data->in_min[i] = via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 VIA686A_REG_IN_MIN
690 (i));
691 data->in_max[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400692 via686a_read_value(data, VIA686A_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
694 for (i = 1; i <= 2; i++) {
695 data->fan[i - 1] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400696 via686a_read_value(data, VIA686A_REG_FAN(i));
697 data->fan_min[i - 1] = via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 VIA686A_REG_FAN_MIN(i));
699 }
700 for (i = 0; i <= 2; i++) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400701 data->temp[i] = via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200702 VIA686A_REG_TEMP[i]) << 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 data->temp_over[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400704 via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200705 VIA686A_REG_TEMP_OVER[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 data->temp_hyst[i] =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400707 via686a_read_value(data,
Jean Delvare563db2f2005-05-17 22:38:57 +0200708 VIA686A_REG_TEMP_HYST[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 }
Jean Delvarebe8992c2005-05-16 19:00:52 +0200710 /* add in lower 2 bits
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
712 temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
713 temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
714 */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400715 data->temp[0] |= (via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 VIA686A_REG_TEMP_LOW1)
717 & 0xc0) >> 6;
718 data->temp[1] |=
Jean Delvare2ec342e2007-06-09 10:11:16 -0400719 (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 0x30) >> 4;
721 data->temp[2] |=
Jean Delvare2ec342e2007-06-09 10:11:16 -0400722 (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 0xc0) >> 6;
724
Jean Delvare2ec342e2007-06-09 10:11:16 -0400725 i = via686a_read_value(data, VIA686A_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 data->fan_div[0] = (i >> 4) & 0x03;
727 data->fan_div[1] = i >> 6;
728 data->alarms =
Jean Delvare2ec342e2007-06-09 10:11:16 -0400729 via686a_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 VIA686A_REG_ALARM1) |
Jean Delvare2ec342e2007-06-09 10:11:16 -0400731 (via686a_read_value(data, VIA686A_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 data->last_updated = jiffies;
733 data->valid = 1;
734 }
735
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100736 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
738 return data;
739}
740
741static struct pci_device_id via686a_pci_ids[] = {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200742 { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
743 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744};
745
746MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
747
Jean Delvare2ec342e2007-06-09 10:11:16 -0400748static int __devinit via686a_device_add(unsigned short address)
749{
750 struct resource res = {
751 .start = address,
752 .end = address + VIA686A_EXTENT - 1,
753 .name = "via686a",
754 .flags = IORESOURCE_IO,
755 };
756 int err;
757
758 pdev = platform_device_alloc("via686a", address);
759 if (!pdev) {
760 err = -ENOMEM;
761 printk(KERN_ERR "via686a: Device allocation failed\n");
762 goto exit;
763 }
764
765 err = platform_device_add_resources(pdev, &res, 1);
766 if (err) {
767 printk(KERN_ERR "via686a: Device resource addition failed "
768 "(%d)\n", err);
769 goto exit_device_put;
770 }
771
772 err = platform_device_add(pdev);
773 if (err) {
774 printk(KERN_ERR "via686a: Device addition failed (%d)\n",
775 err);
776 goto exit_device_put;
777 }
778
779 return 0;
780
781exit_device_put:
782 platform_device_put(pdev);
783exit:
784 return err;
785}
786
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787static int __devinit via686a_pci_probe(struct pci_dev *dev,
Jean Delvarebe8992c2005-05-16 19:00:52 +0200788 const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
Jean Delvare2ec342e2007-06-09 10:11:16 -0400790 u16 address, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Jean Delvare2ec342e2007-06-09 10:11:16 -0400792 if (force_addr) {
793 address = force_addr & ~(VIA686A_EXTENT - 1);
794 dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
795 if (PCIBIOS_SUCCESSFUL !=
796 pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
797 return -ENODEV;
798 }
Jean Delvarebe8992c2005-05-16 19:00:52 +0200799 if (PCIBIOS_SUCCESSFUL !=
800 pci_read_config_word(dev, VIA686A_BASE_REG, &val))
801 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Jean Delvare2d8672c2005-07-19 23:56:35 +0200803 address = val & ~(VIA686A_EXTENT - 1);
Jean Delvare2ec342e2007-06-09 10:11:16 -0400804 if (address == 0) {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200805 dev_err(&dev->dev, "base address not set - upgrade BIOS "
806 "or use force_addr=0xaddr\n");
807 return -ENODEV;
808 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Jean Delvare2ec342e2007-06-09 10:11:16 -0400810 if (PCIBIOS_SUCCESSFUL !=
811 pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
812 return -ENODEV;
813 if (!(val & 0x0001)) {
814 if (!force_addr) {
815 dev_warn(&dev->dev, "Sensors disabled, enable "
816 "with force_addr=0x%x\n", address);
817 return -ENODEV;
818 }
819
820 dev_warn(&dev->dev, "Enabling sensors\n");
821 if (PCIBIOS_SUCCESSFUL !=
822 pci_write_config_word(dev, VIA686A_ENABLE_REG,
823 val | 0x0001))
824 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 }
826
Jean Delvare2ec342e2007-06-09 10:11:16 -0400827 if (platform_driver_register(&via686a_driver))
828 goto exit;
829
830 /* Sets global pdev as a side effect */
831 if (via686a_device_add(address))
832 goto exit_unregister;
833
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 /* Always return failure here. This is to allow other drivers to bind
835 * to this pci device. We don't really want to have control over the
836 * pci device, we only wanted to read as few register values from it.
837 */
Jean Delvare2ec342e2007-06-09 10:11:16 -0400838 s_bridge = pci_dev_get(dev);
839 return -ENODEV;
840
841exit_unregister:
842 platform_driver_unregister(&via686a_driver);
843exit:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 return -ENODEV;
845}
846
847static struct pci_driver via686a_pci_driver = {
Jean Delvarebe8992c2005-05-16 19:00:52 +0200848 .name = "via686a",
849 .id_table = via686a_pci_ids,
850 .probe = via686a_pci_probe,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851};
852
853static int __init sm_via686a_init(void)
854{
Jean Delvarebe8992c2005-05-16 19:00:52 +0200855 return pci_register_driver(&via686a_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
857
858static void __exit sm_via686a_exit(void)
859{
860 pci_unregister_driver(&via686a_pci_driver);
861 if (s_bridge != NULL) {
Jean Delvare2ec342e2007-06-09 10:11:16 -0400862 platform_device_unregister(pdev);
863 platform_driver_unregister(&via686a_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 pci_dev_put(s_bridge);
865 s_bridge = NULL;
866 }
867}
868
869MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
Jean Delvarebe8992c2005-05-16 19:00:52 +0200870 "Mark Studebaker <mdsxyz123@yahoo.com> "
871 "and Bob Dougherty <bobd@stanford.edu>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872MODULE_DESCRIPTION("VIA 686A Sensor device");
873MODULE_LICENSE("GPL");
874
875module_init(sm_via686a_init);
876module_exit(sm_via686a_exit);