xref: /openbmc/linux/drivers/hwmon/via686a.c (revision 2ec342e68453d9f3a1ac28ab80ffa8faacf58710)
18d5d45fbSJean Delvare /*
28d5d45fbSJean Delvare     via686a.c - Part of lm_sensors, Linux kernel modules
38d5d45fbSJean Delvare 		for hardware monitoring
48d5d45fbSJean Delvare 
58d5d45fbSJean Delvare     Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
68d5d45fbSJean Delvare 			Ky�sti M�lkki <kmalkki@cc.hut.fi>,
78d5d45fbSJean Delvare 			Mark Studebaker <mdsxyz123@yahoo.com>,
88d5d45fbSJean Delvare 			and Bob Dougherty <bobd@stanford.edu>
98d5d45fbSJean Delvare     (Some conversion-factor data were contributed by Jonathan Teh Soon Yew
108d5d45fbSJean Delvare     <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
118d5d45fbSJean Delvare 
128d5d45fbSJean Delvare     This program is free software; you can redistribute it and/or modify
138d5d45fbSJean Delvare     it under the terms of the GNU General Public License as published by
148d5d45fbSJean Delvare     the Free Software Foundation; either version 2 of the License, or
158d5d45fbSJean Delvare     (at your option) any later version.
168d5d45fbSJean Delvare 
178d5d45fbSJean Delvare     This program is distributed in the hope that it will be useful,
188d5d45fbSJean Delvare     but WITHOUT ANY WARRANTY; without even the implied warranty of
198d5d45fbSJean Delvare     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
208d5d45fbSJean Delvare     GNU General Public License for more details.
218d5d45fbSJean Delvare 
228d5d45fbSJean Delvare     You should have received a copy of the GNU General Public License
238d5d45fbSJean Delvare     along with this program; if not, write to the Free Software
248d5d45fbSJean Delvare     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
258d5d45fbSJean Delvare */
268d5d45fbSJean Delvare 
278d5d45fbSJean Delvare /*
288d5d45fbSJean Delvare     Supports the Via VT82C686A, VT82C686B south bridges.
298d5d45fbSJean Delvare     Reports all as a 686A.
308d5d45fbSJean Delvare     Warning - only supports a single device.
318d5d45fbSJean Delvare */
328d5d45fbSJean Delvare 
338d5d45fbSJean Delvare #include <linux/module.h>
348d5d45fbSJean Delvare #include <linux/slab.h>
358d5d45fbSJean Delvare #include <linux/pci.h>
368d5d45fbSJean Delvare #include <linux/jiffies.h>
37*2ec342e6SJean Delvare #include <linux/platform_device.h>
38943b0830SMark M. Hoffman #include <linux/hwmon.h>
39943b0830SMark M. Hoffman #include <linux/err.h>
408d5d45fbSJean Delvare #include <linux/init.h>
419a61bf63SIngo Molnar #include <linux/mutex.h>
42a5ebe668SJean Delvare #include <linux/sysfs.h>
438d5d45fbSJean Delvare #include <asm/io.h>
448d5d45fbSJean Delvare 
458d5d45fbSJean Delvare 
468d5d45fbSJean Delvare /* If force_addr is set to anything different from 0, we forcibly enable
478d5d45fbSJean Delvare    the device at the given address. */
4802002963SJean Delvare static unsigned short force_addr;
498d5d45fbSJean Delvare module_param(force_addr, ushort, 0);
508d5d45fbSJean Delvare MODULE_PARM_DESC(force_addr,
518d5d45fbSJean Delvare 		 "Initialize the base address of the sensors");
528d5d45fbSJean Delvare 
53*2ec342e6SJean Delvare static struct platform_device *pdev;
548d5d45fbSJean Delvare 
558d5d45fbSJean Delvare /*
568d5d45fbSJean Delvare    The Via 686a southbridge has a LM78-like chip integrated on the same IC.
578d5d45fbSJean Delvare    This driver is a customized copy of lm78.c
588d5d45fbSJean Delvare */
598d5d45fbSJean Delvare 
608d5d45fbSJean Delvare /* Many VIA686A constants specified below */
618d5d45fbSJean Delvare 
628d5d45fbSJean Delvare /* Length of ISA address segment */
638d5d45fbSJean Delvare #define VIA686A_EXTENT		0x80
648d5d45fbSJean Delvare #define VIA686A_BASE_REG	0x70
658d5d45fbSJean Delvare #define VIA686A_ENABLE_REG	0x74
668d5d45fbSJean Delvare 
678d5d45fbSJean Delvare /* The VIA686A registers */
688d5d45fbSJean Delvare /* ins numbered 0-4 */
698d5d45fbSJean Delvare #define VIA686A_REG_IN_MAX(nr)	(0x2b + ((nr) * 2))
708d5d45fbSJean Delvare #define VIA686A_REG_IN_MIN(nr)	(0x2c + ((nr) * 2))
718d5d45fbSJean Delvare #define VIA686A_REG_IN(nr)	(0x22 + (nr))
728d5d45fbSJean Delvare 
738d5d45fbSJean Delvare /* fans numbered 1-2 */
748d5d45fbSJean Delvare #define VIA686A_REG_FAN_MIN(nr)	(0x3a + (nr))
758d5d45fbSJean Delvare #define VIA686A_REG_FAN(nr)	(0x28 + (nr))
768d5d45fbSJean Delvare 
778d5d45fbSJean Delvare /* temps numbered 1-3 */
788d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP[]	= { 0x20, 0x21, 0x1f };
798d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP_OVER[]	= { 0x39, 0x3d, 0x1d };
808d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP_HYST[]	= { 0x3a, 0x3e, 0x1e };
818d5d45fbSJean Delvare /* bits 7-6 */
828d5d45fbSJean Delvare #define VIA686A_REG_TEMP_LOW1	0x4b
838d5d45fbSJean Delvare /* 2 = bits 5-4, 3 = bits 7-6 */
848d5d45fbSJean Delvare #define VIA686A_REG_TEMP_LOW23	0x49
858d5d45fbSJean Delvare 
868d5d45fbSJean Delvare #define VIA686A_REG_ALARM1	0x41
878d5d45fbSJean Delvare #define VIA686A_REG_ALARM2	0x42
888d5d45fbSJean Delvare #define VIA686A_REG_FANDIV	0x47
898d5d45fbSJean Delvare #define VIA686A_REG_CONFIG	0x40
908d5d45fbSJean Delvare /* The following register sets temp interrupt mode (bits 1-0 for temp1,
918d5d45fbSJean Delvare  3-2 for temp2, 5-4 for temp3).  Modes are:
928d5d45fbSJean Delvare     00 interrupt stays as long as value is out-of-range
938d5d45fbSJean Delvare     01 interrupt is cleared once register is read (default)
948d5d45fbSJean Delvare     10 comparator mode- like 00, but ignores hysteresis
958d5d45fbSJean Delvare     11 same as 00 */
968d5d45fbSJean Delvare #define VIA686A_REG_TEMP_MODE		0x4b
978d5d45fbSJean Delvare /* We'll just assume that you want to set all 3 simultaneously: */
988d5d45fbSJean Delvare #define VIA686A_TEMP_MODE_MASK		0x3F
998d5d45fbSJean Delvare #define VIA686A_TEMP_MODE_CONTINUOUS	0x00
1008d5d45fbSJean Delvare 
1018d5d45fbSJean Delvare /* Conversions. Limit checking is only done on the TO_REG
1028d5d45fbSJean Delvare    variants.
1038d5d45fbSJean Delvare 
1048d5d45fbSJean Delvare ********* VOLTAGE CONVERSIONS (Bob Dougherty) ********
1058d5d45fbSJean Delvare  From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
1068d5d45fbSJean Delvare  voltagefactor[0]=1.25/2628; (2628/1.25=2102.4)   // Vccp
1078d5d45fbSJean Delvare  voltagefactor[1]=1.25/2628; (2628/1.25=2102.4)   // +2.5V
1088d5d45fbSJean Delvare  voltagefactor[2]=1.67/2628; (2628/1.67=1573.7)   // +3.3V
1098d5d45fbSJean Delvare  voltagefactor[3]=2.6/2628;  (2628/2.60=1010.8)   // +5V
1108d5d45fbSJean Delvare  voltagefactor[4]=6.3/2628;  (2628/6.30=417.14)   // +12V
1118d5d45fbSJean Delvare  in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
1128d5d45fbSJean Delvare  That is:
1138d5d45fbSJean Delvare  volts = (25*regVal+133)*factor
1148d5d45fbSJean Delvare  regVal = (volts/factor-133)/25
1158d5d45fbSJean Delvare  (These conversions were contributed by Jonathan Teh Soon Yew
1168d5d45fbSJean Delvare  <j.teh@iname.com>) */
1178d5d45fbSJean Delvare static inline u8 IN_TO_REG(long val, int inNum)
1188d5d45fbSJean Delvare {
1198d5d45fbSJean Delvare 	/* To avoid floating point, we multiply constants by 10 (100 for +12V).
1208d5d45fbSJean Delvare 	   Rounding is done (120500 is actually 133000 - 12500).
1218d5d45fbSJean Delvare 	   Remember that val is expressed in 0.001V/bit, which is why we divide
1228d5d45fbSJean Delvare 	   by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
1238d5d45fbSJean Delvare 	   for the constants. */
1248d5d45fbSJean Delvare 	if (inNum <= 1)
1258d5d45fbSJean Delvare 		return (u8)
1268d5d45fbSJean Delvare 		    SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255);
1278d5d45fbSJean Delvare 	else if (inNum == 2)
1288d5d45fbSJean Delvare 		return (u8)
1298d5d45fbSJean Delvare 		    SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255);
1308d5d45fbSJean Delvare 	else if (inNum == 3)
1318d5d45fbSJean Delvare 		return (u8)
1328d5d45fbSJean Delvare 		    SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255);
1338d5d45fbSJean Delvare 	else
1348d5d45fbSJean Delvare 		return (u8)
1358d5d45fbSJean Delvare 		    SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255);
1368d5d45fbSJean Delvare }
1378d5d45fbSJean Delvare 
1388d5d45fbSJean Delvare static inline long IN_FROM_REG(u8 val, int inNum)
1398d5d45fbSJean Delvare {
1408d5d45fbSJean Delvare 	/* To avoid floating point, we multiply constants by 10 (100 for +12V).
1418d5d45fbSJean Delvare 	   We also multiply them by 1000 because we want 0.001V/bit for the
1428d5d45fbSJean Delvare 	   output value. Rounding is done. */
1438d5d45fbSJean Delvare 	if (inNum <= 1)
1448d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
1458d5d45fbSJean Delvare 	else if (inNum == 2)
1468d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
1478d5d45fbSJean Delvare 	else if (inNum == 3)
1488d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
1498d5d45fbSJean Delvare 	else
1508d5d45fbSJean Delvare 		return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
1518d5d45fbSJean Delvare }
1528d5d45fbSJean Delvare 
1538d5d45fbSJean Delvare /********* FAN RPM CONVERSIONS ********/
1548d5d45fbSJean Delvare /* Higher register values = slower fans (the fan's strobe gates a counter).
1558d5d45fbSJean Delvare  But this chip saturates back at 0, not at 255 like all the other chips.
1568d5d45fbSJean Delvare  So, 0 means 0 RPM */
1578d5d45fbSJean Delvare static inline u8 FAN_TO_REG(long rpm, int div)
1588d5d45fbSJean Delvare {
1598d5d45fbSJean Delvare 	if (rpm == 0)
1608d5d45fbSJean Delvare 		return 0;
1618d5d45fbSJean Delvare 	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
1628d5d45fbSJean Delvare 	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
1638d5d45fbSJean Delvare }
1648d5d45fbSJean Delvare 
1658d5d45fbSJean Delvare #define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
1668d5d45fbSJean Delvare 
1678d5d45fbSJean Delvare /******** TEMP CONVERSIONS (Bob Dougherty) *********/
1688d5d45fbSJean Delvare /* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
1698d5d45fbSJean Delvare       if(temp<169)
1708d5d45fbSJean Delvare 	      return double(temp)*0.427-32.08;
1718d5d45fbSJean Delvare       else if(temp>=169 && temp<=202)
1728d5d45fbSJean Delvare 	      return double(temp)*0.582-58.16;
1738d5d45fbSJean Delvare       else
1748d5d45fbSJean Delvare 	      return double(temp)*0.924-127.33;
1758d5d45fbSJean Delvare 
1768d5d45fbSJean Delvare  A fifth-order polynomial fits the unofficial data (provided by Alex van
1778d5d45fbSJean Delvare  Kaam <darkside@chello.nl>) a bit better.  It also give more reasonable
1788d5d45fbSJean Delvare  numbers on my machine (ie. they agree with what my BIOS tells me).
1798d5d45fbSJean Delvare  Here's the fifth-order fit to the 8-bit data:
1808d5d45fbSJean Delvare  temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
1818d5d45fbSJean Delvare 	2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
1828d5d45fbSJean Delvare 
1838d5d45fbSJean Delvare  (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
1848d5d45fbSJean Delvare  finding my typos in this formula!)
1858d5d45fbSJean Delvare 
1868d5d45fbSJean Delvare  Alas, none of the elegant function-fit solutions will work because we
1878d5d45fbSJean Delvare  aren't allowed to use floating point in the kernel and doing it with
1888d5d45fbSJean Delvare  integers doesn't provide enough precision.  So we'll do boring old
1898d5d45fbSJean Delvare  look-up table stuff.  The unofficial data (see below) have effectively
1908d5d45fbSJean Delvare  7-bit resolution (they are rounded to the nearest degree).  I'm assuming
1918d5d45fbSJean Delvare  that the transfer function of the device is monotonic and smooth, so a
1928d5d45fbSJean Delvare  smooth function fit to the data will allow us to get better precision.
1938d5d45fbSJean Delvare  I used the 5th-order poly fit described above and solved for
1948d5d45fbSJean Delvare  VIA register values 0-255.  I *10 before rounding, so we get tenth-degree
1958d5d45fbSJean Delvare  precision.  (I could have done all 1024 values for our 10-bit readings,
1968d5d45fbSJean Delvare  but the function is very linear in the useful range (0-80 deg C), so
1978d5d45fbSJean Delvare  we'll just use linear interpolation for 10-bit readings.)  So, tempLUT
1988d5d45fbSJean Delvare  is the temp at via register values 0-255: */
199088341bdSJean Delvare static const s16 tempLUT[] =
2008d5d45fbSJean Delvare { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
2018d5d45fbSJean Delvare 	-503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
2028d5d45fbSJean Delvare 	-362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
2038d5d45fbSJean Delvare 	-255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
2048d5d45fbSJean Delvare 	-173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
2058d5d45fbSJean Delvare 	-108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
2068d5d45fbSJean Delvare 	-44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
2078d5d45fbSJean Delvare 	20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
2088d5d45fbSJean Delvare 	88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
2098d5d45fbSJean Delvare 	142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
2108d5d45fbSJean Delvare 	193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
2118d5d45fbSJean Delvare 	245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
2128d5d45fbSJean Delvare 	299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
2138d5d45fbSJean Delvare 	353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
2148d5d45fbSJean Delvare 	409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
2158d5d45fbSJean Delvare 	469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
2168d5d45fbSJean Delvare 	538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
2178d5d45fbSJean Delvare 	621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
2188d5d45fbSJean Delvare 	728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
2198d5d45fbSJean Delvare 	870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
2208d5d45fbSJean Delvare 	1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
2218d5d45fbSJean Delvare 	1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
2228d5d45fbSJean Delvare };
2238d5d45fbSJean Delvare 
2248d5d45fbSJean Delvare /* the original LUT values from Alex van Kaam <darkside@chello.nl>
2258d5d45fbSJean Delvare    (for via register values 12-240):
2268d5d45fbSJean Delvare {-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
2278d5d45fbSJean Delvare -30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
2288d5d45fbSJean Delvare -15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
2298d5d45fbSJean Delvare -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,
2308d5d45fbSJean Delvare 12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
2318d5d45fbSJean Delvare 22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
2328d5d45fbSJean Delvare 33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
2338d5d45fbSJean Delvare 45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
2348d5d45fbSJean Delvare 61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
2358d5d45fbSJean Delvare 85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
2368d5d45fbSJean Delvare 
2378d5d45fbSJean Delvare 
2388d5d45fbSJean Delvare  Here's the reverse LUT.  I got it by doing a 6-th order poly fit (needed
2398d5d45fbSJean Delvare  an extra term for a good fit to these inverse data!) and then
2408d5d45fbSJean Delvare  solving for each temp value from -50 to 110 (the useable range for
2418d5d45fbSJean Delvare  this chip).  Here's the fit:
2428d5d45fbSJean Delvare  viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
2438d5d45fbSJean Delvare  - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
2448d5d45fbSJean Delvare  Note that n=161: */
2458d5d45fbSJean Delvare static const u8 viaLUT[] =
2468d5d45fbSJean Delvare { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
2478d5d45fbSJean Delvare 	23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
2488d5d45fbSJean Delvare 	41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
2498d5d45fbSJean Delvare 	69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
2508d5d45fbSJean Delvare 	103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
2518d5d45fbSJean Delvare 	131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
2528d5d45fbSJean Delvare 	158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
2538d5d45fbSJean Delvare 	182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
2548d5d45fbSJean Delvare 	200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
2558d5d45fbSJean Delvare 	214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
2568d5d45fbSJean Delvare 	225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
2578d5d45fbSJean Delvare 	233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
2588d5d45fbSJean Delvare 	239, 240
2598d5d45fbSJean Delvare };
2608d5d45fbSJean Delvare 
2618d5d45fbSJean Delvare /* Converting temps to (8-bit) hyst and over registers
2628d5d45fbSJean Delvare    No interpolation here.
2638d5d45fbSJean Delvare    The +50 is because the temps start at -50 */
2648d5d45fbSJean Delvare static inline u8 TEMP_TO_REG(long val)
2658d5d45fbSJean Delvare {
2668d5d45fbSJean Delvare 	return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
2678d5d45fbSJean Delvare 		      (val < 0 ? val - 500 : val + 500) / 1000 + 50];
2688d5d45fbSJean Delvare }
2698d5d45fbSJean Delvare 
2708d5d45fbSJean Delvare /* for 8-bit temperature hyst and over registers */
271088341bdSJean Delvare #define TEMP_FROM_REG(val)	((long)tempLUT[val] * 100)
2728d5d45fbSJean Delvare 
2738d5d45fbSJean Delvare /* for 10-bit temperature readings */
2748d5d45fbSJean Delvare static inline long TEMP_FROM_REG10(u16 val)
2758d5d45fbSJean Delvare {
2768d5d45fbSJean Delvare 	u16 eightBits = val >> 2;
2778d5d45fbSJean Delvare 	u16 twoBits = val & 3;
2788d5d45fbSJean Delvare 
2798d5d45fbSJean Delvare 	/* no interpolation for these */
2808d5d45fbSJean Delvare 	if (twoBits == 0 || eightBits == 255)
2818d5d45fbSJean Delvare 		return TEMP_FROM_REG(eightBits);
2828d5d45fbSJean Delvare 
2838d5d45fbSJean Delvare 	/* do some linear interpolation */
2848d5d45fbSJean Delvare 	return (tempLUT[eightBits] * (4 - twoBits) +
2858d5d45fbSJean Delvare 		tempLUT[eightBits + 1] * twoBits) * 25;
2868d5d45fbSJean Delvare }
2878d5d45fbSJean Delvare 
2888d5d45fbSJean Delvare #define DIV_FROM_REG(val) (1 << (val))
2898d5d45fbSJean Delvare #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
2908d5d45fbSJean Delvare 
291ed6bafbfSJean Delvare /* For each registered chip, we need to keep some data in memory.
292ed6bafbfSJean Delvare    The structure is dynamically allocated. */
2938d5d45fbSJean Delvare struct via686a_data {
294*2ec342e6SJean Delvare 	unsigned short addr;
295*2ec342e6SJean Delvare 	const char *name;
296943b0830SMark M. Hoffman 	struct class_device *class_dev;
2979a61bf63SIngo Molnar 	struct mutex update_lock;
2988d5d45fbSJean Delvare 	char valid;		/* !=0 if following fields are valid */
2998d5d45fbSJean Delvare 	unsigned long last_updated;	/* In jiffies */
3008d5d45fbSJean Delvare 
3018d5d45fbSJean Delvare 	u8 in[5];		/* Register value */
3028d5d45fbSJean Delvare 	u8 in_max[5];		/* Register value */
3038d5d45fbSJean Delvare 	u8 in_min[5];		/* Register value */
3048d5d45fbSJean Delvare 	u8 fan[2];		/* Register value */
3058d5d45fbSJean Delvare 	u8 fan_min[2];		/* Register value */
3068d5d45fbSJean Delvare 	u16 temp[3];		/* Register value 10 bit */
3078d5d45fbSJean Delvare 	u8 temp_over[3];	/* Register value */
3088d5d45fbSJean Delvare 	u8 temp_hyst[3];	/* Register value */
3098d5d45fbSJean Delvare 	u8 fan_div[2];		/* Register encoding, shifted right */
3108d5d45fbSJean Delvare 	u16 alarms;		/* Register encoding, combined */
3118d5d45fbSJean Delvare };
3128d5d45fbSJean Delvare 
3138d5d45fbSJean Delvare static struct pci_dev *s_bridge;	/* pointer to the (only) via686a */
3148d5d45fbSJean Delvare 
315*2ec342e6SJean Delvare static int via686a_probe(struct platform_device *pdev);
316*2ec342e6SJean Delvare static int via686a_remove(struct platform_device *pdev);
3178d5d45fbSJean Delvare 
318*2ec342e6SJean Delvare static inline int via686a_read_value(struct via686a_data *data, u8 reg)
3198d5d45fbSJean Delvare {
320*2ec342e6SJean Delvare 	return inb_p(data->addr + reg);
3218d5d45fbSJean Delvare }
3228d5d45fbSJean Delvare 
323*2ec342e6SJean Delvare static inline void via686a_write_value(struct via686a_data *data, u8 reg,
3248d5d45fbSJean Delvare 				       u8 value)
3258d5d45fbSJean Delvare {
326*2ec342e6SJean Delvare 	outb_p(value, data->addr + reg);
3278d5d45fbSJean Delvare }
3288d5d45fbSJean Delvare 
3298d5d45fbSJean Delvare static struct via686a_data *via686a_update_device(struct device *dev);
330*2ec342e6SJean Delvare static void via686a_init_device(struct via686a_data *data);
3318d5d45fbSJean Delvare 
3328d5d45fbSJean Delvare /* following are the sysfs callback functions */
3338d5d45fbSJean Delvare 
3348d5d45fbSJean Delvare /* 7 voltage sensors */
3358d5d45fbSJean Delvare static ssize_t show_in(struct device *dev, char *buf, int nr) {
3368d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3378d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
3388d5d45fbSJean Delvare }
3398d5d45fbSJean Delvare 
3408d5d45fbSJean Delvare static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
3418d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3428d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
3438d5d45fbSJean Delvare }
3448d5d45fbSJean Delvare 
3458d5d45fbSJean Delvare static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
3468d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3478d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
3488d5d45fbSJean Delvare }
3498d5d45fbSJean Delvare 
3508d5d45fbSJean Delvare static ssize_t set_in_min(struct device *dev, const char *buf,
3518d5d45fbSJean Delvare 		size_t count, int nr) {
352*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
3538d5d45fbSJean Delvare 	unsigned long val = simple_strtoul(buf, NULL, 10);
3548d5d45fbSJean Delvare 
3559a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
3568d5d45fbSJean Delvare 	data->in_min[nr] = IN_TO_REG(val, nr);
357*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
3588d5d45fbSJean Delvare 			data->in_min[nr]);
3599a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
3608d5d45fbSJean Delvare 	return count;
3618d5d45fbSJean Delvare }
3628d5d45fbSJean Delvare static ssize_t set_in_max(struct device *dev, const char *buf,
3638d5d45fbSJean Delvare 		size_t count, int nr) {
364*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
3658d5d45fbSJean Delvare 	unsigned long val = simple_strtoul(buf, NULL, 10);
3668d5d45fbSJean Delvare 
3679a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
3688d5d45fbSJean Delvare 	data->in_max[nr] = IN_TO_REG(val, nr);
369*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
3708d5d45fbSJean Delvare 			data->in_max[nr]);
3719a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
3728d5d45fbSJean Delvare 	return count;
3738d5d45fbSJean Delvare }
3748d5d45fbSJean Delvare #define show_in_offset(offset)					\
3758d5d45fbSJean Delvare static ssize_t 							\
3768d5d45fbSJean Delvare 	show_in##offset (struct device *dev, struct device_attribute *attr, char *buf)		\
3778d5d45fbSJean Delvare {								\
3788d5d45fbSJean Delvare 	return show_in(dev, buf, offset);			\
3798d5d45fbSJean Delvare }								\
3808d5d45fbSJean Delvare static ssize_t 							\
3818d5d45fbSJean Delvare 	show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf)	\
3828d5d45fbSJean Delvare {								\
3838d5d45fbSJean Delvare 	return show_in_min(dev, buf, offset);		\
3848d5d45fbSJean Delvare }								\
3858d5d45fbSJean Delvare static ssize_t 							\
3868d5d45fbSJean Delvare 	show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf)	\
3878d5d45fbSJean Delvare {								\
3888d5d45fbSJean Delvare 	return show_in_max(dev, buf, offset);		\
3898d5d45fbSJean Delvare }								\
3908d5d45fbSJean Delvare static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, 	\
3918d5d45fbSJean Delvare 		const char *buf, size_t count) 			\
3928d5d45fbSJean Delvare {								\
3938d5d45fbSJean Delvare 	return set_in_min(dev, buf, count, offset);		\
3948d5d45fbSJean Delvare }								\
3958d5d45fbSJean Delvare static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr,	\
3968d5d45fbSJean Delvare 			const char *buf, size_t count)		\
3978d5d45fbSJean Delvare {								\
3988d5d45fbSJean Delvare 	return set_in_max(dev, buf, count, offset);		\
3998d5d45fbSJean Delvare }								\
4008d5d45fbSJean Delvare static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\
4018d5d45fbSJean Delvare static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, 	\
4028d5d45fbSJean Delvare 		show_in##offset##_min, set_in##offset##_min);	\
4038d5d45fbSJean Delvare static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, 	\
4048d5d45fbSJean Delvare 		show_in##offset##_max, set_in##offset##_max);
4058d5d45fbSJean Delvare 
4068d5d45fbSJean Delvare show_in_offset(0);
4078d5d45fbSJean Delvare show_in_offset(1);
4088d5d45fbSJean Delvare show_in_offset(2);
4098d5d45fbSJean Delvare show_in_offset(3);
4108d5d45fbSJean Delvare show_in_offset(4);
4118d5d45fbSJean Delvare 
4128d5d45fbSJean Delvare /* 3 temperatures */
4138d5d45fbSJean Delvare static ssize_t show_temp(struct device *dev, char *buf, int nr) {
4148d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4158d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
4168d5d45fbSJean Delvare }
4178d5d45fbSJean Delvare static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
4188d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4198d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
4208d5d45fbSJean Delvare }
4218d5d45fbSJean Delvare static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
4228d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4238d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
4248d5d45fbSJean Delvare }
4258d5d45fbSJean Delvare static ssize_t set_temp_over(struct device *dev, const char *buf,
4268d5d45fbSJean Delvare 		size_t count, int nr) {
427*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
4288d5d45fbSJean Delvare 	int val = simple_strtol(buf, NULL, 10);
4298d5d45fbSJean Delvare 
4309a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
4318d5d45fbSJean Delvare 	data->temp_over[nr] = TEMP_TO_REG(val);
432*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
4338d5d45fbSJean Delvare 			    data->temp_over[nr]);
4349a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4358d5d45fbSJean Delvare 	return count;
4368d5d45fbSJean Delvare }
4378d5d45fbSJean Delvare static ssize_t set_temp_hyst(struct device *dev, const char *buf,
4388d5d45fbSJean Delvare 		size_t count, int nr) {
439*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
4408d5d45fbSJean Delvare 	int val = simple_strtol(buf, NULL, 10);
4418d5d45fbSJean Delvare 
4429a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
4438d5d45fbSJean Delvare 	data->temp_hyst[nr] = TEMP_TO_REG(val);
444*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
4458d5d45fbSJean Delvare 			    data->temp_hyst[nr]);
4469a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4478d5d45fbSJean Delvare 	return count;
4488d5d45fbSJean Delvare }
4498d5d45fbSJean Delvare #define show_temp_offset(offset)					\
4508d5d45fbSJean Delvare static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf)	\
4518d5d45fbSJean Delvare {									\
4528d5d45fbSJean Delvare 	return show_temp(dev, buf, offset - 1);				\
4538d5d45fbSJean Delvare }									\
4548d5d45fbSJean Delvare static ssize_t								\
4558d5d45fbSJean Delvare show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf)		\
4568d5d45fbSJean Delvare {									\
4578d5d45fbSJean Delvare 	return show_temp_over(dev, buf, offset - 1);			\
4588d5d45fbSJean Delvare }									\
4598d5d45fbSJean Delvare static ssize_t								\
4608d5d45fbSJean Delvare show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf)		\
4618d5d45fbSJean Delvare {									\
4628d5d45fbSJean Delvare 	return show_temp_hyst(dev, buf, offset - 1);			\
4638d5d45fbSJean Delvare }									\
4648d5d45fbSJean Delvare static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, 		\
4658d5d45fbSJean Delvare 		const char *buf, size_t count) 				\
4668d5d45fbSJean Delvare {									\
4678d5d45fbSJean Delvare 	return set_temp_over(dev, buf, count, offset - 1);		\
4688d5d45fbSJean Delvare }									\
4698d5d45fbSJean Delvare static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, 		\
4708d5d45fbSJean Delvare 		const char *buf, size_t count) 				\
4718d5d45fbSJean Delvare {									\
4728d5d45fbSJean Delvare 	return set_temp_hyst(dev, buf, count, offset - 1);		\
4738d5d45fbSJean Delvare }									\
4748d5d45fbSJean Delvare static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\
4758d5d45fbSJean Delvare static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, 		\
4768d5d45fbSJean Delvare 		show_temp_##offset##_over, set_temp_##offset##_over);	\
4778d5d45fbSJean Delvare static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, 		\
4788d5d45fbSJean Delvare 		show_temp_##offset##_hyst, set_temp_##offset##_hyst);
4798d5d45fbSJean Delvare 
4808d5d45fbSJean Delvare show_temp_offset(1);
4818d5d45fbSJean Delvare show_temp_offset(2);
4828d5d45fbSJean Delvare show_temp_offset(3);
4838d5d45fbSJean Delvare 
4848d5d45fbSJean Delvare /* 2 Fans */
4858d5d45fbSJean Delvare static ssize_t show_fan(struct device *dev, char *buf, int nr) {
4868d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4878d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
4888d5d45fbSJean Delvare 				DIV_FROM_REG(data->fan_div[nr])) );
4898d5d45fbSJean Delvare }
4908d5d45fbSJean Delvare static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
4918d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4928d5d45fbSJean Delvare 	return sprintf(buf, "%d\n",
4938d5d45fbSJean Delvare 		FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
4948d5d45fbSJean Delvare }
4958d5d45fbSJean Delvare static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
4968d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4978d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
4988d5d45fbSJean Delvare }
4998d5d45fbSJean Delvare static ssize_t set_fan_min(struct device *dev, const char *buf,
5008d5d45fbSJean Delvare 		size_t count, int nr) {
501*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
5028d5d45fbSJean Delvare 	int val = simple_strtol(buf, NULL, 10);
5038d5d45fbSJean Delvare 
5049a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
5058d5d45fbSJean Delvare 	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
506*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
5079a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
5088d5d45fbSJean Delvare 	return count;
5098d5d45fbSJean Delvare }
5108d5d45fbSJean Delvare static ssize_t set_fan_div(struct device *dev, const char *buf,
5118d5d45fbSJean Delvare 		size_t count, int nr) {
512*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
5138d5d45fbSJean Delvare 	int val = simple_strtol(buf, NULL, 10);
5148d5d45fbSJean Delvare 	int old;
5158d5d45fbSJean Delvare 
5169a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
517*2ec342e6SJean Delvare 	old = via686a_read_value(data, VIA686A_REG_FANDIV);
5188d5d45fbSJean Delvare 	data->fan_div[nr] = DIV_TO_REG(val);
5198d5d45fbSJean Delvare 	old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
520*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_FANDIV, old);
5219a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
5228d5d45fbSJean Delvare 	return count;
5238d5d45fbSJean Delvare }
5248d5d45fbSJean Delvare 
5258d5d45fbSJean Delvare #define show_fan_offset(offset)						\
5268d5d45fbSJean Delvare static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf)	\
5278d5d45fbSJean Delvare {									\
5288d5d45fbSJean Delvare 	return show_fan(dev, buf, offset - 1);				\
5298d5d45fbSJean Delvare }									\
5308d5d45fbSJean Delvare static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf)	\
5318d5d45fbSJean Delvare {									\
5328d5d45fbSJean Delvare 	return show_fan_min(dev, buf, offset - 1);			\
5338d5d45fbSJean Delvare }									\
5348d5d45fbSJean Delvare static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf)	\
5358d5d45fbSJean Delvare {									\
5368d5d45fbSJean Delvare 	return show_fan_div(dev, buf, offset - 1);			\
5378d5d45fbSJean Delvare }									\
5388d5d45fbSJean Delvare static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, 		\
5398d5d45fbSJean Delvare 	const char *buf, size_t count) 					\
5408d5d45fbSJean Delvare {									\
5418d5d45fbSJean Delvare 	return set_fan_min(dev, buf, count, offset - 1);		\
5428d5d45fbSJean Delvare }									\
5438d5d45fbSJean Delvare static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, 		\
5448d5d45fbSJean Delvare 		const char *buf, size_t count) 				\
5458d5d45fbSJean Delvare {									\
5468d5d45fbSJean Delvare 	return set_fan_div(dev, buf, count, offset - 1);		\
5478d5d45fbSJean Delvare }									\
5488d5d45fbSJean Delvare static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
5498d5d45fbSJean Delvare static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, 		\
5508d5d45fbSJean Delvare 		show_fan_##offset##_min, set_fan_##offset##_min);	\
5518d5d45fbSJean Delvare static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, 		\
5528d5d45fbSJean Delvare 		show_fan_##offset##_div, set_fan_##offset##_div);
5538d5d45fbSJean Delvare 
5548d5d45fbSJean Delvare show_fan_offset(1);
5558d5d45fbSJean Delvare show_fan_offset(2);
5568d5d45fbSJean Delvare 
5578d5d45fbSJean Delvare /* Alarms */
5588d5d45fbSJean Delvare static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) {
5598d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
5608d5d45fbSJean Delvare 	return sprintf(buf, "%u\n", data->alarms);
5618d5d45fbSJean Delvare }
5628d5d45fbSJean Delvare static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
5638d5d45fbSJean Delvare 
564*2ec342e6SJean Delvare static ssize_t show_name(struct device *dev, struct device_attribute
565*2ec342e6SJean Delvare 			 *devattr, char *buf)
566*2ec342e6SJean Delvare {
567*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
568*2ec342e6SJean Delvare 	return sprintf(buf, "%s\n", data->name);
569*2ec342e6SJean Delvare }
570*2ec342e6SJean Delvare static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
571*2ec342e6SJean Delvare 
572a5ebe668SJean Delvare static struct attribute *via686a_attributes[] = {
573a5ebe668SJean Delvare 	&dev_attr_in0_input.attr,
574a5ebe668SJean Delvare 	&dev_attr_in1_input.attr,
575a5ebe668SJean Delvare 	&dev_attr_in2_input.attr,
576a5ebe668SJean Delvare 	&dev_attr_in3_input.attr,
577a5ebe668SJean Delvare 	&dev_attr_in4_input.attr,
578a5ebe668SJean Delvare 	&dev_attr_in0_min.attr,
579a5ebe668SJean Delvare 	&dev_attr_in1_min.attr,
580a5ebe668SJean Delvare 	&dev_attr_in2_min.attr,
581a5ebe668SJean Delvare 	&dev_attr_in3_min.attr,
582a5ebe668SJean Delvare 	&dev_attr_in4_min.attr,
583a5ebe668SJean Delvare 	&dev_attr_in0_max.attr,
584a5ebe668SJean Delvare 	&dev_attr_in1_max.attr,
585a5ebe668SJean Delvare 	&dev_attr_in2_max.attr,
586a5ebe668SJean Delvare 	&dev_attr_in3_max.attr,
587a5ebe668SJean Delvare 	&dev_attr_in4_max.attr,
588a5ebe668SJean Delvare 
589a5ebe668SJean Delvare 	&dev_attr_temp1_input.attr,
590a5ebe668SJean Delvare 	&dev_attr_temp2_input.attr,
591a5ebe668SJean Delvare 	&dev_attr_temp3_input.attr,
592a5ebe668SJean Delvare 	&dev_attr_temp1_max.attr,
593a5ebe668SJean Delvare 	&dev_attr_temp2_max.attr,
594a5ebe668SJean Delvare 	&dev_attr_temp3_max.attr,
595a5ebe668SJean Delvare 	&dev_attr_temp1_max_hyst.attr,
596a5ebe668SJean Delvare 	&dev_attr_temp2_max_hyst.attr,
597a5ebe668SJean Delvare 	&dev_attr_temp3_max_hyst.attr,
598a5ebe668SJean Delvare 
599a5ebe668SJean Delvare 	&dev_attr_fan1_input.attr,
600a5ebe668SJean Delvare 	&dev_attr_fan2_input.attr,
601a5ebe668SJean Delvare 	&dev_attr_fan1_min.attr,
602a5ebe668SJean Delvare 	&dev_attr_fan2_min.attr,
603a5ebe668SJean Delvare 	&dev_attr_fan1_div.attr,
604a5ebe668SJean Delvare 	&dev_attr_fan2_div.attr,
605a5ebe668SJean Delvare 
606a5ebe668SJean Delvare 	&dev_attr_alarms.attr,
607*2ec342e6SJean Delvare 	&dev_attr_name.attr,
608a5ebe668SJean Delvare 	NULL
609a5ebe668SJean Delvare };
610a5ebe668SJean Delvare 
611a5ebe668SJean Delvare static const struct attribute_group via686a_group = {
612a5ebe668SJean Delvare 	.attrs = via686a_attributes,
613a5ebe668SJean Delvare };
614a5ebe668SJean Delvare 
615*2ec342e6SJean Delvare static struct platform_driver via686a_driver = {
616cdaf7934SLaurent Riffard 	.driver = {
61787218842SJean Delvare 		.owner	= THIS_MODULE,
6188d5d45fbSJean Delvare 		.name	= "via686a",
619cdaf7934SLaurent Riffard 	},
620*2ec342e6SJean Delvare 	.probe		= via686a_probe,
621*2ec342e6SJean Delvare 	.remove		= __devexit_p(via686a_remove),
6228d5d45fbSJean Delvare };
6238d5d45fbSJean Delvare 
6248d5d45fbSJean Delvare 
6258d5d45fbSJean Delvare /* This is called when the module is loaded */
626*2ec342e6SJean Delvare static int __devinit via686a_probe(struct platform_device *pdev)
6278d5d45fbSJean Delvare {
6288d5d45fbSJean Delvare 	struct via686a_data *data;
629*2ec342e6SJean Delvare 	struct resource *res;
630*2ec342e6SJean Delvare 	int err;
6318d5d45fbSJean Delvare 
6328d5d45fbSJean Delvare 	/* Reserve the ISA region */
633*2ec342e6SJean Delvare 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
634*2ec342e6SJean Delvare 	if (!request_region(res->start, VIA686A_EXTENT,
635cdaf7934SLaurent Riffard 			    via686a_driver.driver.name)) {
636*2ec342e6SJean Delvare 		dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
637*2ec342e6SJean Delvare 			(unsigned long)res->start, (unsigned long)res->end);
6388d5d45fbSJean Delvare 		return -ENODEV;
6398d5d45fbSJean Delvare 	}
6408d5d45fbSJean Delvare 
641ba9c2e8dSDeepak Saxena 	if (!(data = kzalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
6428d5d45fbSJean Delvare 		err = -ENOMEM;
643943b0830SMark M. Hoffman 		goto exit_release;
6448d5d45fbSJean Delvare 	}
6458d5d45fbSJean Delvare 
646*2ec342e6SJean Delvare 	platform_set_drvdata(pdev, data);
647*2ec342e6SJean Delvare 	data->addr = res->start;
648*2ec342e6SJean Delvare 	data->name = "via686a";
6499a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
6508d5d45fbSJean Delvare 
6518d5d45fbSJean Delvare 	/* Initialize the VIA686A chip */
652*2ec342e6SJean Delvare 	via686a_init_device(data);
6538d5d45fbSJean Delvare 
6548d5d45fbSJean Delvare 	/* Register sysfs hooks */
655*2ec342e6SJean Delvare 	if ((err = sysfs_create_group(&pdev->dev.kobj, &via686a_group)))
656*2ec342e6SJean Delvare 		goto exit_free;
657a5ebe668SJean Delvare 
658*2ec342e6SJean Delvare 	data->class_dev = hwmon_device_register(&pdev->dev);
659943b0830SMark M. Hoffman 	if (IS_ERR(data->class_dev)) {
660943b0830SMark M. Hoffman 		err = PTR_ERR(data->class_dev);
661a5ebe668SJean Delvare 		goto exit_remove_files;
662943b0830SMark M. Hoffman 	}
663943b0830SMark M. Hoffman 
6648d5d45fbSJean Delvare 	return 0;
6658d5d45fbSJean Delvare 
666a5ebe668SJean Delvare exit_remove_files:
667*2ec342e6SJean Delvare 	sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
668943b0830SMark M. Hoffman exit_free:
6698d5d45fbSJean Delvare 	kfree(data);
670943b0830SMark M. Hoffman exit_release:
671*2ec342e6SJean Delvare 	release_region(res->start, VIA686A_EXTENT);
6728d5d45fbSJean Delvare 	return err;
6738d5d45fbSJean Delvare }
6748d5d45fbSJean Delvare 
675*2ec342e6SJean Delvare static int __devexit via686a_remove(struct platform_device *pdev)
6768d5d45fbSJean Delvare {
677*2ec342e6SJean Delvare 	struct via686a_data *data = platform_get_drvdata(pdev);
6788d5d45fbSJean Delvare 
679943b0830SMark M. Hoffman 	hwmon_device_unregister(data->class_dev);
680*2ec342e6SJean Delvare 	sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
681943b0830SMark M. Hoffman 
682*2ec342e6SJean Delvare 	release_region(data->addr, VIA686A_EXTENT);
683*2ec342e6SJean Delvare 	platform_set_drvdata(pdev, NULL);
684943b0830SMark M. Hoffman 	kfree(data);
6858d5d45fbSJean Delvare 
6868d5d45fbSJean Delvare 	return 0;
6878d5d45fbSJean Delvare }
6888d5d45fbSJean Delvare 
689*2ec342e6SJean Delvare static void __devinit via686a_init_device(struct via686a_data *data)
6908d5d45fbSJean Delvare {
6918d5d45fbSJean Delvare 	u8 reg;
6928d5d45fbSJean Delvare 
6938d5d45fbSJean Delvare 	/* Start monitoring */
694*2ec342e6SJean Delvare 	reg = via686a_read_value(data, VIA686A_REG_CONFIG);
695*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F);
6968d5d45fbSJean Delvare 
6978d5d45fbSJean Delvare 	/* Configure temp interrupt mode for continuous-interrupt operation */
698*2ec342e6SJean Delvare 	reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE);
699*2ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_MODE,
70058fe0809SJean Delvare 			    (reg & ~VIA686A_TEMP_MODE_MASK)
70158fe0809SJean Delvare 			    | VIA686A_TEMP_MODE_CONTINUOUS);
7028d5d45fbSJean Delvare }
7038d5d45fbSJean Delvare 
7048d5d45fbSJean Delvare static struct via686a_data *via686a_update_device(struct device *dev)
7058d5d45fbSJean Delvare {
706*2ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
7078d5d45fbSJean Delvare 	int i;
7088d5d45fbSJean Delvare 
7099a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
7108d5d45fbSJean Delvare 
7118d5d45fbSJean Delvare 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
7128d5d45fbSJean Delvare 	    || !data->valid) {
7138d5d45fbSJean Delvare 		for (i = 0; i <= 4; i++) {
7148d5d45fbSJean Delvare 			data->in[i] =
715*2ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_IN(i));
716*2ec342e6SJean Delvare 			data->in_min[i] = via686a_read_value(data,
7178d5d45fbSJean Delvare 							     VIA686A_REG_IN_MIN
7188d5d45fbSJean Delvare 							     (i));
7198d5d45fbSJean Delvare 			data->in_max[i] =
720*2ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_IN_MAX(i));
7218d5d45fbSJean Delvare 		}
7228d5d45fbSJean Delvare 		for (i = 1; i <= 2; i++) {
7238d5d45fbSJean Delvare 			data->fan[i - 1] =
724*2ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_FAN(i));
725*2ec342e6SJean Delvare 			data->fan_min[i - 1] = via686a_read_value(data,
7268d5d45fbSJean Delvare 						     VIA686A_REG_FAN_MIN(i));
7278d5d45fbSJean Delvare 		}
7288d5d45fbSJean Delvare 		for (i = 0; i <= 2; i++) {
729*2ec342e6SJean Delvare 			data->temp[i] = via686a_read_value(data,
7308d5d45fbSJean Delvare 						 VIA686A_REG_TEMP[i]) << 2;
7318d5d45fbSJean Delvare 			data->temp_over[i] =
732*2ec342e6SJean Delvare 			    via686a_read_value(data,
7338d5d45fbSJean Delvare 					       VIA686A_REG_TEMP_OVER[i]);
7348d5d45fbSJean Delvare 			data->temp_hyst[i] =
735*2ec342e6SJean Delvare 			    via686a_read_value(data,
7368d5d45fbSJean Delvare 					       VIA686A_REG_TEMP_HYST[i]);
7378d5d45fbSJean Delvare 		}
7388d5d45fbSJean Delvare 		/* add in lower 2 bits
7398d5d45fbSJean Delvare 		   temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
7408d5d45fbSJean Delvare 		   temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
7418d5d45fbSJean Delvare 		   temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
7428d5d45fbSJean Delvare 		 */
743*2ec342e6SJean Delvare 		data->temp[0] |= (via686a_read_value(data,
7448d5d45fbSJean Delvare 						     VIA686A_REG_TEMP_LOW1)
7458d5d45fbSJean Delvare 				  & 0xc0) >> 6;
7468d5d45fbSJean Delvare 		data->temp[1] |=
747*2ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
7488d5d45fbSJean Delvare 		     0x30) >> 4;
7498d5d45fbSJean Delvare 		data->temp[2] |=
750*2ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
7518d5d45fbSJean Delvare 		     0xc0) >> 6;
7528d5d45fbSJean Delvare 
753*2ec342e6SJean Delvare 		i = via686a_read_value(data, VIA686A_REG_FANDIV);
7548d5d45fbSJean Delvare 		data->fan_div[0] = (i >> 4) & 0x03;
7558d5d45fbSJean Delvare 		data->fan_div[1] = i >> 6;
7568d5d45fbSJean Delvare 		data->alarms =
757*2ec342e6SJean Delvare 		    via686a_read_value(data,
7588d5d45fbSJean Delvare 				       VIA686A_REG_ALARM1) |
759*2ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_ALARM2) << 8);
7608d5d45fbSJean Delvare 		data->last_updated = jiffies;
7618d5d45fbSJean Delvare 		data->valid = 1;
7628d5d45fbSJean Delvare 	}
7638d5d45fbSJean Delvare 
7649a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
7658d5d45fbSJean Delvare 
7668d5d45fbSJean Delvare 	return data;
7678d5d45fbSJean Delvare }
7688d5d45fbSJean Delvare 
7698d5d45fbSJean Delvare static struct pci_device_id via686a_pci_ids[] = {
7708d5d45fbSJean Delvare 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
7718d5d45fbSJean Delvare 	{ 0, }
7728d5d45fbSJean Delvare };
7738d5d45fbSJean Delvare 
7748d5d45fbSJean Delvare MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
7758d5d45fbSJean Delvare 
776*2ec342e6SJean Delvare static int __devinit via686a_device_add(unsigned short address)
777*2ec342e6SJean Delvare {
778*2ec342e6SJean Delvare 	struct resource res = {
779*2ec342e6SJean Delvare 		.start	= address,
780*2ec342e6SJean Delvare 		.end	= address + VIA686A_EXTENT - 1,
781*2ec342e6SJean Delvare 		.name	= "via686a",
782*2ec342e6SJean Delvare 		.flags	= IORESOURCE_IO,
783*2ec342e6SJean Delvare 	};
784*2ec342e6SJean Delvare 	int err;
785*2ec342e6SJean Delvare 
786*2ec342e6SJean Delvare 	pdev = platform_device_alloc("via686a", address);
787*2ec342e6SJean Delvare 	if (!pdev) {
788*2ec342e6SJean Delvare 		err = -ENOMEM;
789*2ec342e6SJean Delvare 		printk(KERN_ERR "via686a: Device allocation failed\n");
790*2ec342e6SJean Delvare 		goto exit;
791*2ec342e6SJean Delvare 	}
792*2ec342e6SJean Delvare 
793*2ec342e6SJean Delvare 	err = platform_device_add_resources(pdev, &res, 1);
794*2ec342e6SJean Delvare 	if (err) {
795*2ec342e6SJean Delvare 		printk(KERN_ERR "via686a: Device resource addition failed "
796*2ec342e6SJean Delvare 		       "(%d)\n", err);
797*2ec342e6SJean Delvare 		goto exit_device_put;
798*2ec342e6SJean Delvare 	}
799*2ec342e6SJean Delvare 
800*2ec342e6SJean Delvare 	err = platform_device_add(pdev);
801*2ec342e6SJean Delvare 	if (err) {
802*2ec342e6SJean Delvare 		printk(KERN_ERR "via686a: Device addition failed (%d)\n",
803*2ec342e6SJean Delvare 		       err);
804*2ec342e6SJean Delvare 		goto exit_device_put;
805*2ec342e6SJean Delvare 	}
806*2ec342e6SJean Delvare 
807*2ec342e6SJean Delvare 	return 0;
808*2ec342e6SJean Delvare 
809*2ec342e6SJean Delvare exit_device_put:
810*2ec342e6SJean Delvare 	platform_device_put(pdev);
811*2ec342e6SJean Delvare exit:
812*2ec342e6SJean Delvare 	return err;
813*2ec342e6SJean Delvare }
814*2ec342e6SJean Delvare 
8158d5d45fbSJean Delvare static int __devinit via686a_pci_probe(struct pci_dev *dev,
8168d5d45fbSJean Delvare 				       const struct pci_device_id *id)
8178d5d45fbSJean Delvare {
818*2ec342e6SJean Delvare 	u16 address, val;
8198d5d45fbSJean Delvare 
820*2ec342e6SJean Delvare 	if (force_addr) {
821*2ec342e6SJean Delvare 		address = force_addr & ~(VIA686A_EXTENT - 1);
822*2ec342e6SJean Delvare 		dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
823*2ec342e6SJean Delvare 		if (PCIBIOS_SUCCESSFUL !=
824*2ec342e6SJean Delvare 		    pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
825*2ec342e6SJean Delvare 			return -ENODEV;
826*2ec342e6SJean Delvare 	}
8278d5d45fbSJean Delvare 	if (PCIBIOS_SUCCESSFUL !=
8288d5d45fbSJean Delvare 	    pci_read_config_word(dev, VIA686A_BASE_REG, &val))
8298d5d45fbSJean Delvare 		return -ENODEV;
8308d5d45fbSJean Delvare 
8312d8672c5SJean Delvare 	address = val & ~(VIA686A_EXTENT - 1);
832*2ec342e6SJean Delvare 	if (address == 0) {
8338d5d45fbSJean Delvare 		dev_err(&dev->dev, "base address not set - upgrade BIOS "
8348d5d45fbSJean Delvare 			"or use force_addr=0xaddr\n");
8358d5d45fbSJean Delvare 		return -ENODEV;
8368d5d45fbSJean Delvare 	}
8378d5d45fbSJean Delvare 
838*2ec342e6SJean Delvare 	if (PCIBIOS_SUCCESSFUL !=
839*2ec342e6SJean Delvare 	    pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
840*2ec342e6SJean Delvare 		return -ENODEV;
841*2ec342e6SJean Delvare 	if (!(val & 0x0001)) {
842*2ec342e6SJean Delvare 		if (!force_addr) {
843*2ec342e6SJean Delvare 			dev_warn(&dev->dev, "Sensors disabled, enable "
844*2ec342e6SJean Delvare 				 "with force_addr=0x%x\n", address);
845*2ec342e6SJean Delvare 			return -ENODEV;
8468d5d45fbSJean Delvare 		}
8478d5d45fbSJean Delvare 
848*2ec342e6SJean Delvare 		dev_warn(&dev->dev, "Enabling sensors\n");
849*2ec342e6SJean Delvare 		if (PCIBIOS_SUCCESSFUL !=
850*2ec342e6SJean Delvare 		    pci_write_config_word(dev, VIA686A_ENABLE_REG,
851*2ec342e6SJean Delvare 					  val | 0x0001))
852*2ec342e6SJean Delvare 			return -ENODEV;
853*2ec342e6SJean Delvare 	}
854*2ec342e6SJean Delvare 
855*2ec342e6SJean Delvare 	if (platform_driver_register(&via686a_driver))
856*2ec342e6SJean Delvare 		goto exit;
857*2ec342e6SJean Delvare 
858*2ec342e6SJean Delvare 	/* Sets global pdev as a side effect */
859*2ec342e6SJean Delvare 	if (via686a_device_add(address))
860*2ec342e6SJean Delvare 		goto exit_unregister;
861*2ec342e6SJean Delvare 
8628d5d45fbSJean Delvare 	/* Always return failure here.  This is to allow other drivers to bind
8638d5d45fbSJean Delvare 	 * to this pci device.  We don't really want to have control over the
8648d5d45fbSJean Delvare 	 * pci device, we only wanted to read as few register values from it.
8658d5d45fbSJean Delvare 	 */
866*2ec342e6SJean Delvare 	s_bridge = pci_dev_get(dev);
867*2ec342e6SJean Delvare 	return -ENODEV;
868*2ec342e6SJean Delvare 
869*2ec342e6SJean Delvare exit_unregister:
870*2ec342e6SJean Delvare 	platform_driver_unregister(&via686a_driver);
871*2ec342e6SJean Delvare exit:
8728d5d45fbSJean Delvare 	return -ENODEV;
8738d5d45fbSJean Delvare }
8748d5d45fbSJean Delvare 
8758d5d45fbSJean Delvare static struct pci_driver via686a_pci_driver = {
8768d5d45fbSJean Delvare 	.name		= "via686a",
8778d5d45fbSJean Delvare 	.id_table	= via686a_pci_ids,
8788d5d45fbSJean Delvare 	.probe		= via686a_pci_probe,
8798d5d45fbSJean Delvare };
8808d5d45fbSJean Delvare 
8818d5d45fbSJean Delvare static int __init sm_via686a_init(void)
8828d5d45fbSJean Delvare {
8838d5d45fbSJean Delvare 	return pci_register_driver(&via686a_pci_driver);
8848d5d45fbSJean Delvare }
8858d5d45fbSJean Delvare 
8868d5d45fbSJean Delvare static void __exit sm_via686a_exit(void)
8878d5d45fbSJean Delvare {
8888d5d45fbSJean Delvare 	pci_unregister_driver(&via686a_pci_driver);
8898d5d45fbSJean Delvare 	if (s_bridge != NULL) {
890*2ec342e6SJean Delvare 		platform_device_unregister(pdev);
891*2ec342e6SJean Delvare 		platform_driver_unregister(&via686a_driver);
8928d5d45fbSJean Delvare 		pci_dev_put(s_bridge);
8938d5d45fbSJean Delvare 		s_bridge = NULL;
8948d5d45fbSJean Delvare 	}
8958d5d45fbSJean Delvare }
8968d5d45fbSJean Delvare 
8978d5d45fbSJean Delvare MODULE_AUTHOR("Ky�sti M�lkki <kmalkki@cc.hut.fi>, "
8988d5d45fbSJean Delvare 	      "Mark Studebaker <mdsxyz123@yahoo.com> "
8998d5d45fbSJean Delvare 	      "and Bob Dougherty <bobd@stanford.edu>");
9008d5d45fbSJean Delvare MODULE_DESCRIPTION("VIA 686A Sensor device");
9018d5d45fbSJean Delvare MODULE_LICENSE("GPL");
9028d5d45fbSJean Delvare 
9038d5d45fbSJean Delvare module_init(sm_via686a_init);
9048d5d45fbSJean Delvare module_exit(sm_via686a_exit);
905