xref: /openbmc/linux/drivers/hwmon/via686a.c (revision 088ce2ac9ebac5c74faf4d39083627875fa6f0f0)
18d5d45fbSJean Delvare /*
29004ac81SGuenter Roeck  * via686a.c - Part of lm_sensors, Linux kernel modules
39004ac81SGuenter Roeck  *	       for hardware monitoring
49004ac81SGuenter Roeck  *
59004ac81SGuenter Roeck  * Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
69004ac81SGuenter Roeck  *			      Kyösti Mälkki <kmalkki@cc.hut.fi>,
79004ac81SGuenter Roeck  *			      Mark Studebaker <mdsxyz123@yahoo.com>,
89004ac81SGuenter Roeck  *			      and Bob Dougherty <bobd@stanford.edu>
99004ac81SGuenter Roeck  *
109004ac81SGuenter Roeck  * (Some conversion-factor data were contributed by Jonathan Teh Soon Yew
119004ac81SGuenter Roeck  * <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
129004ac81SGuenter Roeck  *
139004ac81SGuenter Roeck  * This program is free software; you can redistribute it and/or modify
149004ac81SGuenter Roeck  * it under the terms of the GNU General Public License as published by
159004ac81SGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
169004ac81SGuenter Roeck  * (at your option) any later version.
179004ac81SGuenter Roeck  *
189004ac81SGuenter Roeck  * This program is distributed in the hope that it will be useful,
199004ac81SGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
209004ac81SGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
219004ac81SGuenter Roeck  * GNU General Public License for more details.
229004ac81SGuenter Roeck  *
239004ac81SGuenter Roeck  * You should have received a copy of the GNU General Public License
249004ac81SGuenter Roeck  * along with this program; if not, write to the Free Software
259004ac81SGuenter Roeck  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
268d5d45fbSJean Delvare  */
278d5d45fbSJean Delvare 
288d5d45fbSJean Delvare /*
299004ac81SGuenter Roeck  * Supports the Via VT82C686A, VT82C686B south bridges.
309004ac81SGuenter Roeck  * Reports all as a 686A.
319004ac81SGuenter Roeck  * Warning - only supports a single device.
328d5d45fbSJean Delvare  */
338d5d45fbSJean Delvare 
34774f7827SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
35774f7827SJoe Perches 
368d5d45fbSJean Delvare #include <linux/module.h>
378d5d45fbSJean Delvare #include <linux/slab.h>
388d5d45fbSJean Delvare #include <linux/pci.h>
398d5d45fbSJean Delvare #include <linux/jiffies.h>
402ec342e6SJean Delvare #include <linux/platform_device.h>
41943b0830SMark M. Hoffman #include <linux/hwmon.h>
421e71a5a2SJean Delvare #include <linux/hwmon-sysfs.h>
43943b0830SMark M. Hoffman #include <linux/err.h>
448d5d45fbSJean Delvare #include <linux/init.h>
459a61bf63SIngo Molnar #include <linux/mutex.h>
46a5ebe668SJean Delvare #include <linux/sysfs.h>
47b9acb64aSJean Delvare #include <linux/acpi.h>
486055fae8SH Hartley Sweeten #include <linux/io.h>
498d5d45fbSJean Delvare 
508d5d45fbSJean Delvare 
519004ac81SGuenter Roeck /*
529004ac81SGuenter Roeck  * If force_addr is set to anything different from 0, we forcibly enable
539004ac81SGuenter Roeck  * the device at the given address.
549004ac81SGuenter Roeck  */
5502002963SJean Delvare static unsigned short force_addr;
568d5d45fbSJean Delvare module_param(force_addr, ushort, 0);
578d5d45fbSJean Delvare MODULE_PARM_DESC(force_addr,
588d5d45fbSJean Delvare 		 "Initialize the base address of the sensors");
598d5d45fbSJean Delvare 
602ec342e6SJean Delvare static struct platform_device *pdev;
618d5d45fbSJean Delvare 
628d5d45fbSJean Delvare /*
639004ac81SGuenter Roeck  * The Via 686a southbridge has a LM78-like chip integrated on the same IC.
649004ac81SGuenter Roeck  * This driver is a customized copy of lm78.c
658d5d45fbSJean Delvare  */
668d5d45fbSJean Delvare 
678d5d45fbSJean Delvare /* Many VIA686A constants specified below */
688d5d45fbSJean Delvare 
698d5d45fbSJean Delvare /* Length of ISA address segment */
708d5d45fbSJean Delvare #define VIA686A_EXTENT		0x80
718d5d45fbSJean Delvare #define VIA686A_BASE_REG	0x70
728d5d45fbSJean Delvare #define VIA686A_ENABLE_REG	0x74
738d5d45fbSJean Delvare 
748d5d45fbSJean Delvare /* The VIA686A registers */
758d5d45fbSJean Delvare /* ins numbered 0-4 */
768d5d45fbSJean Delvare #define VIA686A_REG_IN_MAX(nr)	(0x2b + ((nr) * 2))
778d5d45fbSJean Delvare #define VIA686A_REG_IN_MIN(nr)	(0x2c + ((nr) * 2))
788d5d45fbSJean Delvare #define VIA686A_REG_IN(nr)	(0x22 + (nr))
798d5d45fbSJean Delvare 
808d5d45fbSJean Delvare /* fans numbered 1-2 */
818d5d45fbSJean Delvare #define VIA686A_REG_FAN_MIN(nr)	(0x3a + (nr))
828d5d45fbSJean Delvare #define VIA686A_REG_FAN(nr)	(0x28 + (nr))
838d5d45fbSJean Delvare 
848d5d45fbSJean Delvare /* temps numbered 1-3 */
858d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP[]	= { 0x20, 0x21, 0x1f };
868d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP_OVER[]	= { 0x39, 0x3d, 0x1d };
878d5d45fbSJean Delvare static const u8 VIA686A_REG_TEMP_HYST[]	= { 0x3a, 0x3e, 0x1e };
888d5d45fbSJean Delvare /* bits 7-6 */
898d5d45fbSJean Delvare #define VIA686A_REG_TEMP_LOW1	0x4b
908d5d45fbSJean Delvare /* 2 = bits 5-4, 3 = bits 7-6 */
918d5d45fbSJean Delvare #define VIA686A_REG_TEMP_LOW23	0x49
928d5d45fbSJean Delvare 
938d5d45fbSJean Delvare #define VIA686A_REG_ALARM1	0x41
948d5d45fbSJean Delvare #define VIA686A_REG_ALARM2	0x42
958d5d45fbSJean Delvare #define VIA686A_REG_FANDIV	0x47
968d5d45fbSJean Delvare #define VIA686A_REG_CONFIG	0x40
979004ac81SGuenter Roeck /*
989004ac81SGuenter Roeck  * The following register sets temp interrupt mode (bits 1-0 for temp1,
999004ac81SGuenter Roeck  * 3-2 for temp2, 5-4 for temp3).  Modes are:
1009004ac81SGuenter Roeck  * 00 interrupt stays as long as value is out-of-range
1019004ac81SGuenter Roeck  * 01 interrupt is cleared once register is read (default)
1029004ac81SGuenter Roeck  * 10 comparator mode- like 00, but ignores hysteresis
1039004ac81SGuenter Roeck  * 11 same as 00
1049004ac81SGuenter Roeck  */
1058d5d45fbSJean Delvare #define VIA686A_REG_TEMP_MODE		0x4b
1068d5d45fbSJean Delvare /* We'll just assume that you want to set all 3 simultaneously: */
1078d5d45fbSJean Delvare #define VIA686A_TEMP_MODE_MASK		0x3F
1088d5d45fbSJean Delvare #define VIA686A_TEMP_MODE_CONTINUOUS	0x00
1098d5d45fbSJean Delvare 
1109004ac81SGuenter Roeck /*
1119004ac81SGuenter Roeck  * Conversions. Limit checking is only done on the TO_REG
1129004ac81SGuenter Roeck  * variants.
1139004ac81SGuenter Roeck  *
1149004ac81SGuenter Roeck  ******** VOLTAGE CONVERSIONS (Bob Dougherty) ********
1159004ac81SGuenter Roeck  * From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
1169004ac81SGuenter Roeck  * voltagefactor[0]=1.25/2628; (2628/1.25=2102.4)   // Vccp
1179004ac81SGuenter Roeck  * voltagefactor[1]=1.25/2628; (2628/1.25=2102.4)   // +2.5V
1189004ac81SGuenter Roeck  * voltagefactor[2]=1.67/2628; (2628/1.67=1573.7)   // +3.3V
1199004ac81SGuenter Roeck  * voltagefactor[3]=2.6/2628;  (2628/2.60=1010.8)   // +5V
1209004ac81SGuenter Roeck  * voltagefactor[4]=6.3/2628;  (2628/6.30=417.14)   // +12V
1219004ac81SGuenter Roeck  * in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
1229004ac81SGuenter Roeck  * That is:
1239004ac81SGuenter Roeck  * volts = (25*regVal+133)*factor
1249004ac81SGuenter Roeck  * regVal = (volts/factor-133)/25
1259004ac81SGuenter Roeck  * (These conversions were contributed by Jonathan Teh Soon Yew
1269004ac81SGuenter Roeck  * <j.teh@iname.com>)
1279004ac81SGuenter Roeck  */
128*088ce2acSGuenter Roeck static inline u8 IN_TO_REG(long val, int in_num)
1298d5d45fbSJean Delvare {
1309004ac81SGuenter Roeck 	/*
1319004ac81SGuenter Roeck 	 * To avoid floating point, we multiply constants by 10 (100 for +12V).
1329004ac81SGuenter Roeck 	 * Rounding is done (120500 is actually 133000 - 12500).
1339004ac81SGuenter Roeck 	 * Remember that val is expressed in 0.001V/bit, which is why we divide
1349004ac81SGuenter Roeck 	 * by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
1359004ac81SGuenter Roeck 	 * for the constants.
1369004ac81SGuenter Roeck 	 */
137*088ce2acSGuenter Roeck 	if (in_num <= 1)
1382a844c14SGuenter Roeck 		return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255);
139*088ce2acSGuenter Roeck 	else if (in_num == 2)
1402a844c14SGuenter Roeck 		return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255);
141*088ce2acSGuenter Roeck 	else if (in_num == 3)
1422a844c14SGuenter Roeck 		return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255);
1438d5d45fbSJean Delvare 	else
1442a844c14SGuenter Roeck 		return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0,
1452a844c14SGuenter Roeck 				      255);
1468d5d45fbSJean Delvare }
1478d5d45fbSJean Delvare 
148*088ce2acSGuenter Roeck static inline long IN_FROM_REG(u8 val, int in_num)
1498d5d45fbSJean Delvare {
1509004ac81SGuenter Roeck 	/*
1519004ac81SGuenter Roeck 	 * To avoid floating point, we multiply constants by 10 (100 for +12V).
1529004ac81SGuenter Roeck 	 * We also multiply them by 1000 because we want 0.001V/bit for the
1539004ac81SGuenter Roeck 	 * output value. Rounding is done.
1549004ac81SGuenter Roeck 	 */
155*088ce2acSGuenter Roeck 	if (in_num <= 1)
1568d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
157*088ce2acSGuenter Roeck 	else if (in_num == 2)
1588d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
159*088ce2acSGuenter Roeck 	else if (in_num == 3)
1608d5d45fbSJean Delvare 		return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
1618d5d45fbSJean Delvare 	else
1628d5d45fbSJean Delvare 		return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
1638d5d45fbSJean Delvare }
1648d5d45fbSJean Delvare 
1658d5d45fbSJean Delvare /********* FAN RPM CONVERSIONS ********/
1669004ac81SGuenter Roeck /*
1679004ac81SGuenter Roeck  * Higher register values = slower fans (the fan's strobe gates a counter).
1689004ac81SGuenter Roeck  * But this chip saturates back at 0, not at 255 like all the other chips.
1699004ac81SGuenter Roeck  * So, 0 means 0 RPM
1709004ac81SGuenter Roeck  */
1718d5d45fbSJean Delvare static inline u8 FAN_TO_REG(long rpm, int div)
1728d5d45fbSJean Delvare {
1738d5d45fbSJean Delvare 	if (rpm == 0)
1748d5d45fbSJean Delvare 		return 0;
1752a844c14SGuenter Roeck 	rpm = clamp_val(rpm, 1, 1000000);
1762a844c14SGuenter Roeck 	return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
1778d5d45fbSJean Delvare }
1788d5d45fbSJean Delvare 
1799004ac81SGuenter Roeck #define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (val) == 255 ? 0 : 1350000 / \
1809004ac81SGuenter Roeck 				((val) * (div)))
1818d5d45fbSJean Delvare 
1828d5d45fbSJean Delvare /******** TEMP CONVERSIONS (Bob Dougherty) *********/
1839004ac81SGuenter Roeck /*
1849004ac81SGuenter Roeck  * linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
1859004ac81SGuenter Roeck  *	if(temp<169)
1869004ac81SGuenter Roeck  *		return double(temp)*0.427-32.08;
1879004ac81SGuenter Roeck  *	else if(temp>=169 && temp<=202)
1889004ac81SGuenter Roeck  *		return double(temp)*0.582-58.16;
1899004ac81SGuenter Roeck  *	else
1909004ac81SGuenter Roeck  *		return double(temp)*0.924-127.33;
1919004ac81SGuenter Roeck  *
1929004ac81SGuenter Roeck  * A fifth-order polynomial fits the unofficial data (provided by Alex van
1939004ac81SGuenter Roeck  * Kaam <darkside@chello.nl>) a bit better.  It also give more reasonable
1949004ac81SGuenter Roeck  * numbers on my machine (ie. they agree with what my BIOS tells me).
1959004ac81SGuenter Roeck  * Here's the fifth-order fit to the 8-bit data:
1969004ac81SGuenter Roeck  * temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
1979004ac81SGuenter Roeck  *	2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
1989004ac81SGuenter Roeck  *
1999004ac81SGuenter Roeck  * (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
2009004ac81SGuenter Roeck  * finding my typos in this formula!)
2019004ac81SGuenter Roeck  *
2029004ac81SGuenter Roeck  * Alas, none of the elegant function-fit solutions will work because we
2039004ac81SGuenter Roeck  * aren't allowed to use floating point in the kernel and doing it with
2049004ac81SGuenter Roeck  * integers doesn't provide enough precision.  So we'll do boring old
2059004ac81SGuenter Roeck  * look-up table stuff.  The unofficial data (see below) have effectively
2069004ac81SGuenter Roeck  * 7-bit resolution (they are rounded to the nearest degree).  I'm assuming
2079004ac81SGuenter Roeck  * that the transfer function of the device is monotonic and smooth, so a
2089004ac81SGuenter Roeck  * smooth function fit to the data will allow us to get better precision.
2099004ac81SGuenter Roeck  * I used the 5th-order poly fit described above and solved for
2109004ac81SGuenter Roeck  * VIA register values 0-255.  I *10 before rounding, so we get tenth-degree
2119004ac81SGuenter Roeck  * precision.  (I could have done all 1024 values for our 10-bit readings,
2129004ac81SGuenter Roeck  * but the function is very linear in the useful range (0-80 deg C), so
213*088ce2acSGuenter Roeck  * we'll just use linear interpolation for 10-bit readings.)  So, temp_lut
2149004ac81SGuenter Roeck  * is the temp at via register values 0-255:
2159004ac81SGuenter Roeck  */
216*088ce2acSGuenter Roeck static const s16 temp_lut[] = {
2179004ac81SGuenter Roeck 	-709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
2188d5d45fbSJean Delvare 	-503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
2198d5d45fbSJean Delvare 	-362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
2208d5d45fbSJean Delvare 	-255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
2218d5d45fbSJean Delvare 	-173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
2228d5d45fbSJean Delvare 	-108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
2238d5d45fbSJean Delvare 	-44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
2248d5d45fbSJean Delvare 	20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
2258d5d45fbSJean Delvare 	88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
2268d5d45fbSJean Delvare 	142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
2278d5d45fbSJean Delvare 	193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
2288d5d45fbSJean Delvare 	245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
2298d5d45fbSJean Delvare 	299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
2308d5d45fbSJean Delvare 	353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
2318d5d45fbSJean Delvare 	409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
2328d5d45fbSJean Delvare 	469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
2338d5d45fbSJean Delvare 	538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
2348d5d45fbSJean Delvare 	621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
2358d5d45fbSJean Delvare 	728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
2368d5d45fbSJean Delvare 	870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
2378d5d45fbSJean Delvare 	1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
2388d5d45fbSJean Delvare 	1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
2398d5d45fbSJean Delvare };
2408d5d45fbSJean Delvare 
2419004ac81SGuenter Roeck /*
2429004ac81SGuenter Roeck  * the original LUT values from Alex van Kaam <darkside@chello.nl>
2439004ac81SGuenter Roeck  * (for via register values 12-240):
2449004ac81SGuenter Roeck  * {-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
2459004ac81SGuenter Roeck  * -30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
2469004ac81SGuenter Roeck  * -15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
2479004ac81SGuenter Roeck  * -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,
2489004ac81SGuenter Roeck  * 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,
2499004ac81SGuenter Roeck  * 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,
2509004ac81SGuenter Roeck  * 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,
2519004ac81SGuenter Roeck  * 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,
2529004ac81SGuenter Roeck  * 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,
2539004ac81SGuenter Roeck  * 85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
2549004ac81SGuenter Roeck  *
2559004ac81SGuenter Roeck  *
2569004ac81SGuenter Roeck  * Here's the reverse LUT.  I got it by doing a 6-th order poly fit (needed
2579004ac81SGuenter Roeck  * an extra term for a good fit to these inverse data!) and then
2589004ac81SGuenter Roeck  * solving for each temp value from -50 to 110 (the useable range for
2599004ac81SGuenter Roeck  * this chip).  Here's the fit:
2609004ac81SGuenter Roeck  * viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
2619004ac81SGuenter Roeck  * - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
2629004ac81SGuenter Roeck  * Note that n=161:
2639004ac81SGuenter Roeck  */
264*088ce2acSGuenter Roeck static const u8 via_lut[] = {
2659004ac81SGuenter Roeck 	12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
2668d5d45fbSJean Delvare 	23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
2678d5d45fbSJean Delvare 	41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
2688d5d45fbSJean Delvare 	69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
2698d5d45fbSJean Delvare 	103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
2708d5d45fbSJean Delvare 	131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
2718d5d45fbSJean Delvare 	158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
2728d5d45fbSJean Delvare 	182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
2738d5d45fbSJean Delvare 	200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
2748d5d45fbSJean Delvare 	214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
2758d5d45fbSJean Delvare 	225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
2768d5d45fbSJean Delvare 	233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
2778d5d45fbSJean Delvare 	239, 240
2788d5d45fbSJean Delvare };
2798d5d45fbSJean Delvare 
2809004ac81SGuenter Roeck /*
2819004ac81SGuenter Roeck  * Converting temps to (8-bit) hyst and over registers
2829004ac81SGuenter Roeck  * No interpolation here.
2839004ac81SGuenter Roeck  * The +50 is because the temps start at -50
2849004ac81SGuenter Roeck  */
2858d5d45fbSJean Delvare static inline u8 TEMP_TO_REG(long val)
2868d5d45fbSJean Delvare {
287*088ce2acSGuenter Roeck 	return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 :
2888d5d45fbSJean Delvare 		      (val < 0 ? val - 500 : val + 500) / 1000 + 50];
2898d5d45fbSJean Delvare }
2908d5d45fbSJean Delvare 
2918d5d45fbSJean Delvare /* for 8-bit temperature hyst and over registers */
292*088ce2acSGuenter Roeck #define TEMP_FROM_REG(val)	((long)temp_lut[val] * 100)
2938d5d45fbSJean Delvare 
2948d5d45fbSJean Delvare /* for 10-bit temperature readings */
2958d5d45fbSJean Delvare static inline long TEMP_FROM_REG10(u16 val)
2968d5d45fbSJean Delvare {
297*088ce2acSGuenter Roeck 	u16 eight_bits = val >> 2;
298*088ce2acSGuenter Roeck 	u16 two_bits = val & 3;
2998d5d45fbSJean Delvare 
3008d5d45fbSJean Delvare 	/* no interpolation for these */
301*088ce2acSGuenter Roeck 	if (two_bits == 0 || eight_bits == 255)
302*088ce2acSGuenter Roeck 		return TEMP_FROM_REG(eight_bits);
3038d5d45fbSJean Delvare 
3048d5d45fbSJean Delvare 	/* do some linear interpolation */
305*088ce2acSGuenter Roeck 	return (temp_lut[eight_bits] * (4 - two_bits) +
306*088ce2acSGuenter Roeck 		temp_lut[eight_bits + 1] * two_bits) * 25;
3078d5d45fbSJean Delvare }
3088d5d45fbSJean Delvare 
3098d5d45fbSJean Delvare #define DIV_FROM_REG(val) (1 << (val))
3108d5d45fbSJean Delvare #define DIV_TO_REG(val) ((val) == 8 ? 3 : (val) == 4 ? 2 : (val) == 1 ? 0 : 1)
3118d5d45fbSJean Delvare 
3129004ac81SGuenter Roeck /*
3139004ac81SGuenter Roeck  * For each registered chip, we need to keep some data in memory.
3149004ac81SGuenter Roeck  * The structure is dynamically allocated.
3159004ac81SGuenter Roeck  */
3168d5d45fbSJean Delvare struct via686a_data {
3172ec342e6SJean Delvare 	unsigned short addr;
3182ec342e6SJean Delvare 	const char *name;
3191beeffe4STony Jones 	struct device *hwmon_dev;
3209a61bf63SIngo Molnar 	struct mutex update_lock;
3218d5d45fbSJean Delvare 	char valid;		/* !=0 if following fields are valid */
3228d5d45fbSJean Delvare 	unsigned long last_updated;	/* In jiffies */
3238d5d45fbSJean Delvare 
3248d5d45fbSJean Delvare 	u8 in[5];		/* Register value */
3258d5d45fbSJean Delvare 	u8 in_max[5];		/* Register value */
3268d5d45fbSJean Delvare 	u8 in_min[5];		/* Register value */
3278d5d45fbSJean Delvare 	u8 fan[2];		/* Register value */
3288d5d45fbSJean Delvare 	u8 fan_min[2];		/* Register value */
3298d5d45fbSJean Delvare 	u16 temp[3];		/* Register value 10 bit */
3308d5d45fbSJean Delvare 	u8 temp_over[3];	/* Register value */
3318d5d45fbSJean Delvare 	u8 temp_hyst[3];	/* Register value */
3328d5d45fbSJean Delvare 	u8 fan_div[2];		/* Register encoding, shifted right */
3338d5d45fbSJean Delvare 	u16 alarms;		/* Register encoding, combined */
3348d5d45fbSJean Delvare };
3358d5d45fbSJean Delvare 
3368d5d45fbSJean Delvare static struct pci_dev *s_bridge;	/* pointer to the (only) via686a */
3378d5d45fbSJean Delvare 
3382ec342e6SJean Delvare static int via686a_probe(struct platform_device *pdev);
339281dfd0bSBill Pemberton static int via686a_remove(struct platform_device *pdev);
3408d5d45fbSJean Delvare 
3412ec342e6SJean Delvare static inline int via686a_read_value(struct via686a_data *data, u8 reg)
3428d5d45fbSJean Delvare {
3432ec342e6SJean Delvare 	return inb_p(data->addr + reg);
3448d5d45fbSJean Delvare }
3458d5d45fbSJean Delvare 
3462ec342e6SJean Delvare static inline void via686a_write_value(struct via686a_data *data, u8 reg,
3478d5d45fbSJean Delvare 				       u8 value)
3488d5d45fbSJean Delvare {
3492ec342e6SJean Delvare 	outb_p(value, data->addr + reg);
3508d5d45fbSJean Delvare }
3518d5d45fbSJean Delvare 
3528d5d45fbSJean Delvare static struct via686a_data *via686a_update_device(struct device *dev);
3532ec342e6SJean Delvare static void via686a_init_device(struct via686a_data *data);
3548d5d45fbSJean Delvare 
3558d5d45fbSJean Delvare /* following are the sysfs callback functions */
3568d5d45fbSJean Delvare 
3578d5d45fbSJean Delvare /* 7 voltage sensors */
3581e71a5a2SJean Delvare static ssize_t show_in(struct device *dev, struct device_attribute *da,
3591e71a5a2SJean Delvare 		char *buf) {
3608d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3611e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3621e71a5a2SJean Delvare 	int nr = attr->index;
3638d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
3648d5d45fbSJean Delvare }
3658d5d45fbSJean Delvare 
3661e71a5a2SJean Delvare static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
3671e71a5a2SJean Delvare 		char *buf) {
3688d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3691e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3701e71a5a2SJean Delvare 	int nr = attr->index;
3718d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
3728d5d45fbSJean Delvare }
3738d5d45fbSJean Delvare 
3741e71a5a2SJean Delvare static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
3751e71a5a2SJean Delvare 		char *buf) {
3768d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
3771e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3781e71a5a2SJean Delvare 	int nr = attr->index;
3798d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
3808d5d45fbSJean Delvare }
3818d5d45fbSJean Delvare 
3821e71a5a2SJean Delvare static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
3831e71a5a2SJean Delvare 		const char *buf, size_t count) {
3842ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
3851e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3861e71a5a2SJean Delvare 	int nr = attr->index;
3879004ac81SGuenter Roeck 	unsigned long val;
3889004ac81SGuenter Roeck 	int err;
3899004ac81SGuenter Roeck 
3909004ac81SGuenter Roeck 	err = kstrtoul(buf, 10, &val);
3919004ac81SGuenter Roeck 	if (err)
3929004ac81SGuenter Roeck 		return err;
3938d5d45fbSJean Delvare 
3949a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
3958d5d45fbSJean Delvare 	data->in_min[nr] = IN_TO_REG(val, nr);
3962ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
3978d5d45fbSJean Delvare 			data->in_min[nr]);
3989a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
3998d5d45fbSJean Delvare 	return count;
4008d5d45fbSJean Delvare }
4011e71a5a2SJean Delvare static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
4021e71a5a2SJean Delvare 		const char *buf, size_t count) {
4032ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
4041e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4051e71a5a2SJean Delvare 	int nr = attr->index;
4069004ac81SGuenter Roeck 	unsigned long val;
4079004ac81SGuenter Roeck 	int err;
4089004ac81SGuenter Roeck 
4099004ac81SGuenter Roeck 	err = kstrtoul(buf, 10, &val);
4109004ac81SGuenter Roeck 	if (err)
4119004ac81SGuenter Roeck 		return err;
4128d5d45fbSJean Delvare 
4139a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
4148d5d45fbSJean Delvare 	data->in_max[nr] = IN_TO_REG(val, nr);
4152ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
4168d5d45fbSJean Delvare 			data->in_max[nr]);
4179a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4188d5d45fbSJean Delvare 	return count;
4198d5d45fbSJean Delvare }
4208d5d45fbSJean Delvare #define show_in_offset(offset)					\
4211e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO,		\
4221e71a5a2SJean Delvare 		show_in, NULL, offset);				\
4231e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR,	\
4241e71a5a2SJean Delvare 		show_in_min, set_in_min, offset);		\
4251e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR,	\
4261e71a5a2SJean Delvare 		show_in_max, set_in_max, offset);
4278d5d45fbSJean Delvare 
4288d5d45fbSJean Delvare show_in_offset(0);
4298d5d45fbSJean Delvare show_in_offset(1);
4308d5d45fbSJean Delvare show_in_offset(2);
4318d5d45fbSJean Delvare show_in_offset(3);
4328d5d45fbSJean Delvare show_in_offset(4);
4338d5d45fbSJean Delvare 
4348d5d45fbSJean Delvare /* 3 temperatures */
4351e71a5a2SJean Delvare static ssize_t show_temp(struct device *dev, struct device_attribute *da,
4361e71a5a2SJean Delvare 		char *buf) {
4378d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4381e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4391e71a5a2SJean Delvare 	int nr = attr->index;
4408d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
4418d5d45fbSJean Delvare }
4421e71a5a2SJean Delvare static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
4431e71a5a2SJean Delvare 		char *buf) {
4448d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4451e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4461e71a5a2SJean Delvare 	int nr = attr->index;
4478d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
4488d5d45fbSJean Delvare }
4491e71a5a2SJean Delvare static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
4501e71a5a2SJean Delvare 		char *buf) {
4518d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
4521e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4531e71a5a2SJean Delvare 	int nr = attr->index;
4548d5d45fbSJean Delvare 	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
4558d5d45fbSJean Delvare }
4561e71a5a2SJean Delvare static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
4571e71a5a2SJean Delvare 		const char *buf, size_t count) {
4582ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
4591e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4601e71a5a2SJean Delvare 	int nr = attr->index;
4619004ac81SGuenter Roeck 	long val;
4629004ac81SGuenter Roeck 	int err;
4639004ac81SGuenter Roeck 
4649004ac81SGuenter Roeck 	err = kstrtol(buf, 10, &val);
4659004ac81SGuenter Roeck 	if (err)
4669004ac81SGuenter Roeck 		return err;
4678d5d45fbSJean Delvare 
4689a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
4698d5d45fbSJean Delvare 	data->temp_over[nr] = TEMP_TO_REG(val);
4702ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
4718d5d45fbSJean Delvare 			    data->temp_over[nr]);
4729a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4738d5d45fbSJean Delvare 	return count;
4748d5d45fbSJean Delvare }
4751e71a5a2SJean Delvare static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
4761e71a5a2SJean Delvare 		const char *buf, size_t count) {
4772ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
4781e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
4791e71a5a2SJean Delvare 	int nr = attr->index;
4809004ac81SGuenter Roeck 	long val;
4819004ac81SGuenter Roeck 	int err;
4829004ac81SGuenter Roeck 
4839004ac81SGuenter Roeck 	err = kstrtol(buf, 10, &val);
4849004ac81SGuenter Roeck 	if (err)
4859004ac81SGuenter Roeck 		return err;
4868d5d45fbSJean Delvare 
4879a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
4888d5d45fbSJean Delvare 	data->temp_hyst[nr] = TEMP_TO_REG(val);
4892ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
4908d5d45fbSJean Delvare 			    data->temp_hyst[nr]);
4919a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4928d5d45fbSJean Delvare 	return count;
4938d5d45fbSJean Delvare }
4948d5d45fbSJean Delvare #define show_temp_offset(offset)					\
4951e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO,		\
4961e71a5a2SJean Delvare 		show_temp, NULL, offset - 1);				\
4971e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,	\
4981e71a5a2SJean Delvare 		show_temp_over, set_temp_over, offset - 1);		\
4991e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR,	\
5001e71a5a2SJean Delvare 		show_temp_hyst, set_temp_hyst, offset - 1);
5018d5d45fbSJean Delvare 
5028d5d45fbSJean Delvare show_temp_offset(1);
5038d5d45fbSJean Delvare show_temp_offset(2);
5048d5d45fbSJean Delvare show_temp_offset(3);
5058d5d45fbSJean Delvare 
5068d5d45fbSJean Delvare /* 2 Fans */
5071e71a5a2SJean Delvare static ssize_t show_fan(struct device *dev, struct device_attribute *da,
5081e71a5a2SJean Delvare 		char *buf) {
5098d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
5101e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
5111e71a5a2SJean Delvare 	int nr = attr->index;
5128d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
5138d5d45fbSJean Delvare 				DIV_FROM_REG(data->fan_div[nr])));
5148d5d45fbSJean Delvare }
5151e71a5a2SJean Delvare static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
5161e71a5a2SJean Delvare 		char *buf) {
5178d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
5181e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
5191e71a5a2SJean Delvare 	int nr = attr->index;
5208d5d45fbSJean Delvare 	return sprintf(buf, "%d\n",
5219004ac81SGuenter Roeck 		FAN_FROM_REG(data->fan_min[nr],
5229004ac81SGuenter Roeck 			     DIV_FROM_REG(data->fan_div[nr])));
5238d5d45fbSJean Delvare }
5241e71a5a2SJean Delvare static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
5251e71a5a2SJean Delvare 		char *buf) {
5268d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
5271e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
5281e71a5a2SJean Delvare 	int nr = attr->index;
5298d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
5308d5d45fbSJean Delvare }
5311e71a5a2SJean Delvare static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
5321e71a5a2SJean Delvare 		const char *buf, size_t count) {
5332ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
5341e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
5351e71a5a2SJean Delvare 	int nr = attr->index;
5369004ac81SGuenter Roeck 	unsigned long val;
5379004ac81SGuenter Roeck 	int err;
5389004ac81SGuenter Roeck 
5399004ac81SGuenter Roeck 	err = kstrtoul(buf, 10, &val);
5409004ac81SGuenter Roeck 	if (err)
5419004ac81SGuenter Roeck 		return err;
5428d5d45fbSJean Delvare 
5439a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
5448d5d45fbSJean Delvare 	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
5452ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
5469a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
5478d5d45fbSJean Delvare 	return count;
5488d5d45fbSJean Delvare }
5491e71a5a2SJean Delvare static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
5501e71a5a2SJean Delvare 		const char *buf, size_t count) {
5512ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
5521e71a5a2SJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
5531e71a5a2SJean Delvare 	int nr = attr->index;
5548d5d45fbSJean Delvare 	int old;
5559004ac81SGuenter Roeck 	unsigned long val;
5569004ac81SGuenter Roeck 	int err;
5579004ac81SGuenter Roeck 
5589004ac81SGuenter Roeck 	err = kstrtoul(buf, 10, &val);
5599004ac81SGuenter Roeck 	if (err)
5609004ac81SGuenter Roeck 		return err;
5618d5d45fbSJean Delvare 
5629a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
5632ec342e6SJean Delvare 	old = via686a_read_value(data, VIA686A_REG_FANDIV);
5648d5d45fbSJean Delvare 	data->fan_div[nr] = DIV_TO_REG(val);
5658d5d45fbSJean Delvare 	old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
5662ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_FANDIV, old);
5679a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
5688d5d45fbSJean Delvare 	return count;
5698d5d45fbSJean Delvare }
5708d5d45fbSJean Delvare 
5718d5d45fbSJean Delvare #define show_fan_offset(offset)						\
5721e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO,			\
5731e71a5a2SJean Delvare 		show_fan, NULL, offset - 1);				\
5741e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\
5751e71a5a2SJean Delvare 		show_fan_min, set_fan_min, offset - 1);			\
5761e71a5a2SJean Delvare static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\
5771e71a5a2SJean Delvare 		show_fan_div, set_fan_div, offset - 1);
5788d5d45fbSJean Delvare 
5798d5d45fbSJean Delvare show_fan_offset(1);
5808d5d45fbSJean Delvare show_fan_offset(2);
5818d5d45fbSJean Delvare 
5828d5d45fbSJean Delvare /* Alarms */
5839004ac81SGuenter Roeck static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
5849004ac81SGuenter Roeck 			   char *buf)
5859004ac81SGuenter Roeck {
5868d5d45fbSJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
5878d5d45fbSJean Delvare 	return sprintf(buf, "%u\n", data->alarms);
5888d5d45fbSJean Delvare }
5899004ac81SGuenter Roeck 
5908d5d45fbSJean Delvare static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
5918d5d45fbSJean Delvare 
59213ff05e9SJean Delvare static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
59313ff05e9SJean Delvare 			  char *buf)
59413ff05e9SJean Delvare {
59513ff05e9SJean Delvare 	int bitnr = to_sensor_dev_attr(attr)->index;
59613ff05e9SJean Delvare 	struct via686a_data *data = via686a_update_device(dev);
59713ff05e9SJean Delvare 	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
59813ff05e9SJean Delvare }
59913ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
60013ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
60113ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
60213ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
60313ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
60413ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
60513ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 11);
60613ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 15);
60713ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
60813ff05e9SJean Delvare static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
60913ff05e9SJean Delvare 
6102ec342e6SJean Delvare static ssize_t show_name(struct device *dev, struct device_attribute
6112ec342e6SJean Delvare 			 *devattr, char *buf)
6122ec342e6SJean Delvare {
6132ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
6142ec342e6SJean Delvare 	return sprintf(buf, "%s\n", data->name);
6152ec342e6SJean Delvare }
6162ec342e6SJean Delvare static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
6172ec342e6SJean Delvare 
618a5ebe668SJean Delvare static struct attribute *via686a_attributes[] = {
6191e71a5a2SJean Delvare 	&sensor_dev_attr_in0_input.dev_attr.attr,
6201e71a5a2SJean Delvare 	&sensor_dev_attr_in1_input.dev_attr.attr,
6211e71a5a2SJean Delvare 	&sensor_dev_attr_in2_input.dev_attr.attr,
6221e71a5a2SJean Delvare 	&sensor_dev_attr_in3_input.dev_attr.attr,
6231e71a5a2SJean Delvare 	&sensor_dev_attr_in4_input.dev_attr.attr,
6241e71a5a2SJean Delvare 	&sensor_dev_attr_in0_min.dev_attr.attr,
6251e71a5a2SJean Delvare 	&sensor_dev_attr_in1_min.dev_attr.attr,
6261e71a5a2SJean Delvare 	&sensor_dev_attr_in2_min.dev_attr.attr,
6271e71a5a2SJean Delvare 	&sensor_dev_attr_in3_min.dev_attr.attr,
6281e71a5a2SJean Delvare 	&sensor_dev_attr_in4_min.dev_attr.attr,
6291e71a5a2SJean Delvare 	&sensor_dev_attr_in0_max.dev_attr.attr,
6301e71a5a2SJean Delvare 	&sensor_dev_attr_in1_max.dev_attr.attr,
6311e71a5a2SJean Delvare 	&sensor_dev_attr_in2_max.dev_attr.attr,
6321e71a5a2SJean Delvare 	&sensor_dev_attr_in3_max.dev_attr.attr,
6331e71a5a2SJean Delvare 	&sensor_dev_attr_in4_max.dev_attr.attr,
63413ff05e9SJean Delvare 	&sensor_dev_attr_in0_alarm.dev_attr.attr,
63513ff05e9SJean Delvare 	&sensor_dev_attr_in1_alarm.dev_attr.attr,
63613ff05e9SJean Delvare 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
63713ff05e9SJean Delvare 	&sensor_dev_attr_in3_alarm.dev_attr.attr,
63813ff05e9SJean Delvare 	&sensor_dev_attr_in4_alarm.dev_attr.attr,
639a5ebe668SJean Delvare 
6401e71a5a2SJean Delvare 	&sensor_dev_attr_temp1_input.dev_attr.attr,
6411e71a5a2SJean Delvare 	&sensor_dev_attr_temp2_input.dev_attr.attr,
6421e71a5a2SJean Delvare 	&sensor_dev_attr_temp3_input.dev_attr.attr,
6431e71a5a2SJean Delvare 	&sensor_dev_attr_temp1_max.dev_attr.attr,
6441e71a5a2SJean Delvare 	&sensor_dev_attr_temp2_max.dev_attr.attr,
6451e71a5a2SJean Delvare 	&sensor_dev_attr_temp3_max.dev_attr.attr,
6461e71a5a2SJean Delvare 	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
6471e71a5a2SJean Delvare 	&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
6481e71a5a2SJean Delvare 	&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
64913ff05e9SJean Delvare 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
65013ff05e9SJean Delvare 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
65113ff05e9SJean Delvare 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
652a5ebe668SJean Delvare 
6531e71a5a2SJean Delvare 	&sensor_dev_attr_fan1_input.dev_attr.attr,
6541e71a5a2SJean Delvare 	&sensor_dev_attr_fan2_input.dev_attr.attr,
6551e71a5a2SJean Delvare 	&sensor_dev_attr_fan1_min.dev_attr.attr,
6561e71a5a2SJean Delvare 	&sensor_dev_attr_fan2_min.dev_attr.attr,
6571e71a5a2SJean Delvare 	&sensor_dev_attr_fan1_div.dev_attr.attr,
6581e71a5a2SJean Delvare 	&sensor_dev_attr_fan2_div.dev_attr.attr,
65913ff05e9SJean Delvare 	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
66013ff05e9SJean Delvare 	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
661a5ebe668SJean Delvare 
662a5ebe668SJean Delvare 	&dev_attr_alarms.attr,
6632ec342e6SJean Delvare 	&dev_attr_name.attr,
664a5ebe668SJean Delvare 	NULL
665a5ebe668SJean Delvare };
666a5ebe668SJean Delvare 
667a5ebe668SJean Delvare static const struct attribute_group via686a_group = {
668a5ebe668SJean Delvare 	.attrs = via686a_attributes,
669a5ebe668SJean Delvare };
670a5ebe668SJean Delvare 
6712ec342e6SJean Delvare static struct platform_driver via686a_driver = {
672cdaf7934SLaurent Riffard 	.driver = {
67387218842SJean Delvare 		.owner	= THIS_MODULE,
6748d5d45fbSJean Delvare 		.name	= "via686a",
675cdaf7934SLaurent Riffard 	},
6762ec342e6SJean Delvare 	.probe		= via686a_probe,
6779e5e9b7aSBill Pemberton 	.remove		= via686a_remove,
6788d5d45fbSJean Delvare };
6798d5d45fbSJean Delvare 
6808d5d45fbSJean Delvare 
6818d5d45fbSJean Delvare /* This is called when the module is loaded */
6826c931ae1SBill Pemberton static int via686a_probe(struct platform_device *pdev)
6838d5d45fbSJean Delvare {
6848d5d45fbSJean Delvare 	struct via686a_data *data;
6852ec342e6SJean Delvare 	struct resource *res;
6862ec342e6SJean Delvare 	int err;
6878d5d45fbSJean Delvare 
6888d5d45fbSJean Delvare 	/* Reserve the ISA region */
6892ec342e6SJean Delvare 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
690fd55bc00SGuenter Roeck 	if (!devm_request_region(&pdev->dev, res->start, VIA686A_EXTENT,
691cdaf7934SLaurent Riffard 				 via686a_driver.driver.name)) {
6922ec342e6SJean Delvare 		dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
6932ec342e6SJean Delvare 			(unsigned long)res->start, (unsigned long)res->end);
6948d5d45fbSJean Delvare 		return -ENODEV;
6958d5d45fbSJean Delvare 	}
6968d5d45fbSJean Delvare 
697fd55bc00SGuenter Roeck 	data = devm_kzalloc(&pdev->dev, sizeof(struct via686a_data),
698fd55bc00SGuenter Roeck 			    GFP_KERNEL);
699fd55bc00SGuenter Roeck 	if (!data)
700fd55bc00SGuenter Roeck 		return -ENOMEM;
7018d5d45fbSJean Delvare 
7022ec342e6SJean Delvare 	platform_set_drvdata(pdev, data);
7032ec342e6SJean Delvare 	data->addr = res->start;
7042ec342e6SJean Delvare 	data->name = "via686a";
7059a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
7068d5d45fbSJean Delvare 
7078d5d45fbSJean Delvare 	/* Initialize the VIA686A chip */
7082ec342e6SJean Delvare 	via686a_init_device(data);
7098d5d45fbSJean Delvare 
7108d5d45fbSJean Delvare 	/* Register sysfs hooks */
7119004ac81SGuenter Roeck 	err = sysfs_create_group(&pdev->dev.kobj, &via686a_group);
7129004ac81SGuenter Roeck 	if (err)
713fd55bc00SGuenter Roeck 		return err;
714a5ebe668SJean Delvare 
7151beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&pdev->dev);
7161beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
7171beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
718a5ebe668SJean Delvare 		goto exit_remove_files;
719943b0830SMark M. Hoffman 	}
720943b0830SMark M. Hoffman 
7218d5d45fbSJean Delvare 	return 0;
7228d5d45fbSJean Delvare 
723a5ebe668SJean Delvare exit_remove_files:
7242ec342e6SJean Delvare 	sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
7258d5d45fbSJean Delvare 	return err;
7268d5d45fbSJean Delvare }
7278d5d45fbSJean Delvare 
728281dfd0bSBill Pemberton static int via686a_remove(struct platform_device *pdev)
7298d5d45fbSJean Delvare {
7302ec342e6SJean Delvare 	struct via686a_data *data = platform_get_drvdata(pdev);
7318d5d45fbSJean Delvare 
7321beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
7332ec342e6SJean Delvare 	sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
734943b0830SMark M. Hoffman 
7358d5d45fbSJean Delvare 	return 0;
7368d5d45fbSJean Delvare }
7378d5d45fbSJean Delvare 
738f790674dSJean Delvare static void via686a_update_fan_div(struct via686a_data *data)
739f790674dSJean Delvare {
740f790674dSJean Delvare 	int reg = via686a_read_value(data, VIA686A_REG_FANDIV);
741f790674dSJean Delvare 	data->fan_div[0] = (reg >> 4) & 0x03;
742f790674dSJean Delvare 	data->fan_div[1] = reg >> 6;
743f790674dSJean Delvare }
744f790674dSJean Delvare 
7456c931ae1SBill Pemberton static void via686a_init_device(struct via686a_data *data)
7468d5d45fbSJean Delvare {
7478d5d45fbSJean Delvare 	u8 reg;
7488d5d45fbSJean Delvare 
7498d5d45fbSJean Delvare 	/* Start monitoring */
7502ec342e6SJean Delvare 	reg = via686a_read_value(data, VIA686A_REG_CONFIG);
7512ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F);
7528d5d45fbSJean Delvare 
7538d5d45fbSJean Delvare 	/* Configure temp interrupt mode for continuous-interrupt operation */
7542ec342e6SJean Delvare 	reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE);
7552ec342e6SJean Delvare 	via686a_write_value(data, VIA686A_REG_TEMP_MODE,
75658fe0809SJean Delvare 			    (reg & ~VIA686A_TEMP_MODE_MASK)
75758fe0809SJean Delvare 			    | VIA686A_TEMP_MODE_CONTINUOUS);
758f790674dSJean Delvare 
759f790674dSJean Delvare 	/* Pre-read fan clock divisor values */
760f790674dSJean Delvare 	via686a_update_fan_div(data);
7618d5d45fbSJean Delvare }
7628d5d45fbSJean Delvare 
7638d5d45fbSJean Delvare static struct via686a_data *via686a_update_device(struct device *dev)
7648d5d45fbSJean Delvare {
7652ec342e6SJean Delvare 	struct via686a_data *data = dev_get_drvdata(dev);
7668d5d45fbSJean Delvare 	int i;
7678d5d45fbSJean Delvare 
7689a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
7698d5d45fbSJean Delvare 
7708d5d45fbSJean Delvare 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
7718d5d45fbSJean Delvare 	    || !data->valid) {
7728d5d45fbSJean Delvare 		for (i = 0; i <= 4; i++) {
7738d5d45fbSJean Delvare 			data->in[i] =
7742ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_IN(i));
7752ec342e6SJean Delvare 			data->in_min[i] = via686a_read_value(data,
7768d5d45fbSJean Delvare 							     VIA686A_REG_IN_MIN
7778d5d45fbSJean Delvare 							     (i));
7788d5d45fbSJean Delvare 			data->in_max[i] =
7792ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_IN_MAX(i));
7808d5d45fbSJean Delvare 		}
7818d5d45fbSJean Delvare 		for (i = 1; i <= 2; i++) {
7828d5d45fbSJean Delvare 			data->fan[i - 1] =
7832ec342e6SJean Delvare 			    via686a_read_value(data, VIA686A_REG_FAN(i));
7842ec342e6SJean Delvare 			data->fan_min[i - 1] = via686a_read_value(data,
7858d5d45fbSJean Delvare 						     VIA686A_REG_FAN_MIN(i));
7868d5d45fbSJean Delvare 		}
7878d5d45fbSJean Delvare 		for (i = 0; i <= 2; i++) {
7882ec342e6SJean Delvare 			data->temp[i] = via686a_read_value(data,
7898d5d45fbSJean Delvare 						 VIA686A_REG_TEMP[i]) << 2;
7908d5d45fbSJean Delvare 			data->temp_over[i] =
7912ec342e6SJean Delvare 			    via686a_read_value(data,
7928d5d45fbSJean Delvare 					       VIA686A_REG_TEMP_OVER[i]);
7938d5d45fbSJean Delvare 			data->temp_hyst[i] =
7942ec342e6SJean Delvare 			    via686a_read_value(data,
7958d5d45fbSJean Delvare 					       VIA686A_REG_TEMP_HYST[i]);
7968d5d45fbSJean Delvare 		}
7979004ac81SGuenter Roeck 		/*
7989004ac81SGuenter Roeck 		 * add in lower 2 bits
7999004ac81SGuenter Roeck 		 * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
8009004ac81SGuenter Roeck 		 * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
8019004ac81SGuenter Roeck 		 * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
8028d5d45fbSJean Delvare 		 */
8032ec342e6SJean Delvare 		data->temp[0] |= (via686a_read_value(data,
8048d5d45fbSJean Delvare 						     VIA686A_REG_TEMP_LOW1)
8058d5d45fbSJean Delvare 				  & 0xc0) >> 6;
8068d5d45fbSJean Delvare 		data->temp[1] |=
8072ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
8088d5d45fbSJean Delvare 		     0x30) >> 4;
8098d5d45fbSJean Delvare 		data->temp[2] |=
8102ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
8118d5d45fbSJean Delvare 		     0xc0) >> 6;
8128d5d45fbSJean Delvare 
813f790674dSJean Delvare 		via686a_update_fan_div(data);
8148d5d45fbSJean Delvare 		data->alarms =
8152ec342e6SJean Delvare 		    via686a_read_value(data,
8168d5d45fbSJean Delvare 				       VIA686A_REG_ALARM1) |
8172ec342e6SJean Delvare 		    (via686a_read_value(data, VIA686A_REG_ALARM2) << 8);
8188d5d45fbSJean Delvare 		data->last_updated = jiffies;
8198d5d45fbSJean Delvare 		data->valid = 1;
8208d5d45fbSJean Delvare 	}
8218d5d45fbSJean Delvare 
8229a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
8238d5d45fbSJean Delvare 
8248d5d45fbSJean Delvare 	return data;
8258d5d45fbSJean Delvare }
8268d5d45fbSJean Delvare 
827600151b9SFrans Meulenbroeks static DEFINE_PCI_DEVICE_TABLE(via686a_pci_ids) = {
8288d5d45fbSJean Delvare 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
8299004ac81SGuenter Roeck 	{ }
8308d5d45fbSJean Delvare };
8318d5d45fbSJean Delvare MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
8328d5d45fbSJean Delvare 
8336c931ae1SBill Pemberton static int via686a_device_add(unsigned short address)
8342ec342e6SJean Delvare {
8352ec342e6SJean Delvare 	struct resource res = {
8362ec342e6SJean Delvare 		.start	= address,
8372ec342e6SJean Delvare 		.end	= address + VIA686A_EXTENT - 1,
8382ec342e6SJean Delvare 		.name	= "via686a",
8392ec342e6SJean Delvare 		.flags	= IORESOURCE_IO,
8402ec342e6SJean Delvare 	};
8412ec342e6SJean Delvare 	int err;
8422ec342e6SJean Delvare 
843b9acb64aSJean Delvare 	err = acpi_check_resource_conflict(&res);
844b9acb64aSJean Delvare 	if (err)
845b9acb64aSJean Delvare 		goto exit;
846b9acb64aSJean Delvare 
8472ec342e6SJean Delvare 	pdev = platform_device_alloc("via686a", address);
8482ec342e6SJean Delvare 	if (!pdev) {
8492ec342e6SJean Delvare 		err = -ENOMEM;
850774f7827SJoe Perches 		pr_err("Device allocation failed\n");
8512ec342e6SJean Delvare 		goto exit;
8522ec342e6SJean Delvare 	}
8532ec342e6SJean Delvare 
8542ec342e6SJean Delvare 	err = platform_device_add_resources(pdev, &res, 1);
8552ec342e6SJean Delvare 	if (err) {
856774f7827SJoe Perches 		pr_err("Device resource addition failed (%d)\n", err);
8572ec342e6SJean Delvare 		goto exit_device_put;
8582ec342e6SJean Delvare 	}
8592ec342e6SJean Delvare 
8602ec342e6SJean Delvare 	err = platform_device_add(pdev);
8612ec342e6SJean Delvare 	if (err) {
862774f7827SJoe Perches 		pr_err("Device addition failed (%d)\n", err);
8632ec342e6SJean Delvare 		goto exit_device_put;
8642ec342e6SJean Delvare 	}
8652ec342e6SJean Delvare 
8662ec342e6SJean Delvare 	return 0;
8672ec342e6SJean Delvare 
8682ec342e6SJean Delvare exit_device_put:
8692ec342e6SJean Delvare 	platform_device_put(pdev);
8702ec342e6SJean Delvare exit:
8712ec342e6SJean Delvare 	return err;
8722ec342e6SJean Delvare }
8732ec342e6SJean Delvare 
8746c931ae1SBill Pemberton static int via686a_pci_probe(struct pci_dev *dev,
8758d5d45fbSJean Delvare 				       const struct pci_device_id *id)
8768d5d45fbSJean Delvare {
8772ec342e6SJean Delvare 	u16 address, val;
8788d5d45fbSJean Delvare 
8792ec342e6SJean Delvare 	if (force_addr) {
8802ec342e6SJean Delvare 		address = force_addr & ~(VIA686A_EXTENT - 1);
8812ec342e6SJean Delvare 		dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
8822ec342e6SJean Delvare 		if (PCIBIOS_SUCCESSFUL !=
8832ec342e6SJean Delvare 		    pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
8842ec342e6SJean Delvare 			return -ENODEV;
8852ec342e6SJean Delvare 	}
8868d5d45fbSJean Delvare 	if (PCIBIOS_SUCCESSFUL !=
8878d5d45fbSJean Delvare 	    pci_read_config_word(dev, VIA686A_BASE_REG, &val))
8888d5d45fbSJean Delvare 		return -ENODEV;
8898d5d45fbSJean Delvare 
8902d8672c5SJean Delvare 	address = val & ~(VIA686A_EXTENT - 1);
8912ec342e6SJean Delvare 	if (address == 0) {
8928d5d45fbSJean Delvare 		dev_err(&dev->dev, "base address not set - upgrade BIOS "
8938d5d45fbSJean Delvare 			"or use force_addr=0xaddr\n");
8948d5d45fbSJean Delvare 		return -ENODEV;
8958d5d45fbSJean Delvare 	}
8968d5d45fbSJean Delvare 
8972ec342e6SJean Delvare 	if (PCIBIOS_SUCCESSFUL !=
8982ec342e6SJean Delvare 	    pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
8992ec342e6SJean Delvare 		return -ENODEV;
9002ec342e6SJean Delvare 	if (!(val & 0x0001)) {
9012ec342e6SJean Delvare 		if (!force_addr) {
9022ec342e6SJean Delvare 			dev_warn(&dev->dev, "Sensors disabled, enable "
9032ec342e6SJean Delvare 				 "with force_addr=0x%x\n", address);
9042ec342e6SJean Delvare 			return -ENODEV;
9058d5d45fbSJean Delvare 		}
9068d5d45fbSJean Delvare 
9072ec342e6SJean Delvare 		dev_warn(&dev->dev, "Enabling sensors\n");
9082ec342e6SJean Delvare 		if (PCIBIOS_SUCCESSFUL !=
9092ec342e6SJean Delvare 		    pci_write_config_word(dev, VIA686A_ENABLE_REG,
9102ec342e6SJean Delvare 					  val | 0x0001))
9112ec342e6SJean Delvare 			return -ENODEV;
9122ec342e6SJean Delvare 	}
9132ec342e6SJean Delvare 
9142ec342e6SJean Delvare 	if (platform_driver_register(&via686a_driver))
9152ec342e6SJean Delvare 		goto exit;
9162ec342e6SJean Delvare 
9172ec342e6SJean Delvare 	/* Sets global pdev as a side effect */
9182ec342e6SJean Delvare 	if (via686a_device_add(address))
9192ec342e6SJean Delvare 		goto exit_unregister;
9202ec342e6SJean Delvare 
9219004ac81SGuenter Roeck 	/*
9229004ac81SGuenter Roeck 	 * Always return failure here.  This is to allow other drivers to bind
9238d5d45fbSJean Delvare 	 * to this pci device.  We don't really want to have control over the
9248d5d45fbSJean Delvare 	 * pci device, we only wanted to read as few register values from it.
9258d5d45fbSJean Delvare 	 */
9262ec342e6SJean Delvare 	s_bridge = pci_dev_get(dev);
9272ec342e6SJean Delvare 	return -ENODEV;
9282ec342e6SJean Delvare 
9292ec342e6SJean Delvare exit_unregister:
9302ec342e6SJean Delvare 	platform_driver_unregister(&via686a_driver);
9312ec342e6SJean Delvare exit:
9328d5d45fbSJean Delvare 	return -ENODEV;
9338d5d45fbSJean Delvare }
9348d5d45fbSJean Delvare 
9358d5d45fbSJean Delvare static struct pci_driver via686a_pci_driver = {
9368d5d45fbSJean Delvare 	.name		= "via686a",
9378d5d45fbSJean Delvare 	.id_table	= via686a_pci_ids,
9388d5d45fbSJean Delvare 	.probe		= via686a_pci_probe,
9398d5d45fbSJean Delvare };
9408d5d45fbSJean Delvare 
9418d5d45fbSJean Delvare static int __init sm_via686a_init(void)
9428d5d45fbSJean Delvare {
9438d5d45fbSJean Delvare 	return pci_register_driver(&via686a_pci_driver);
9448d5d45fbSJean Delvare }
9458d5d45fbSJean Delvare 
9468d5d45fbSJean Delvare static void __exit sm_via686a_exit(void)
9478d5d45fbSJean Delvare {
9488d5d45fbSJean Delvare 	pci_unregister_driver(&via686a_pci_driver);
9498d5d45fbSJean Delvare 	if (s_bridge != NULL) {
9502ec342e6SJean Delvare 		platform_device_unregister(pdev);
9512ec342e6SJean Delvare 		platform_driver_unregister(&via686a_driver);
9528d5d45fbSJean Delvare 		pci_dev_put(s_bridge);
9538d5d45fbSJean Delvare 		s_bridge = NULL;
9548d5d45fbSJean Delvare 	}
9558d5d45fbSJean Delvare }
9568d5d45fbSJean Delvare 
95796de0e25SJan Engelhardt MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
9588d5d45fbSJean Delvare 	      "Mark Studebaker <mdsxyz123@yahoo.com> "
9598d5d45fbSJean Delvare 	      "and Bob Dougherty <bobd@stanford.edu>");
9608d5d45fbSJean Delvare MODULE_DESCRIPTION("VIA 686A Sensor device");
9618d5d45fbSJean Delvare MODULE_LICENSE("GPL");
9628d5d45fbSJean Delvare 
9638d5d45fbSJean Delvare module_init(sm_via686a_init);
9648d5d45fbSJean Delvare module_exit(sm_via686a_exit);
965