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