* [BK PATCH] More i2c driver changes for 2.5.66 @ 2003-04-03 0:14 Greg KH 2003-04-03 0:15 ` [PATCH] " Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:14 UTC (permalink / raw) To: torvalds; +Cc: linux-kernel, sensors Hi, Here are some more i2c driver changes for 2.5.66. It adds two i2c chip drivers, and converts all of them to the sysfs interface. In doing so, I've ripped out all of the old /proc and sysctl mess. A number of small bugfixes for some of the media i2c drivers are in here too. I've also fixed up the bus_id of the i2c client devices, as the current code can cause duplicate bus_ids which didn't play nice on some systems. Please pull from: bk://kernel.bkbits.net/gregkh/linux/i2c-2.5 thanks, greg k-h Documentation/i2c/proc-interface | 53 - drivers/i2c/i2c-proc.c | 187 --- include/linux/i2c-proc.h | 373 ------- Documentation/i2c/sysfs-interface | 177 +++ drivers/i2c/Kconfig | 11 drivers/i2c/Makefile | 2 drivers/i2c/busses/Kconfig | 13 drivers/i2c/chips/Kconfig | 70 + drivers/i2c/chips/Makefile | 2 drivers/i2c/chips/adm1021.c | 360 +++---- drivers/i2c/chips/lm75.c | 187 +-- drivers/i2c/chips/via686a.c | 1010 +++++++++++++++++++- drivers/i2c/chips/w83781d.c | 1892 +++++++++++++++++++++++++++++++++++++- drivers/i2c/i2c-core.c | 190 --- drivers/i2c/i2c-proc.c | 550 ----------- drivers/i2c/i2c-sensor.c | 182 +++ drivers/media/video/adv7175.c | 2 drivers/media/video/tvmixer.c | 20 include/linux/i2c-proc.h | 43 include/linux/i2c-sensor.h | 373 +++++++ include/linux/i2c-vid.h | 66 + 21 files changed, 3975 insertions(+), 1788 deletions(-) ----- <azarah@gentoo.org>: o i2c: w83781d i2c driver updated for 2.5.66-bk4 (with sysfs support, empty tree) <j.dittmer@portrix.net>: o i2c: add i2c-via686a driver o i2c: fix compile bugs in tvmixer.c Greg Kroah-Hartman <greg@kroah.com>: o i2c: remove old proc documentation and add sysfs file documentation o i2c: fix up broken drivers/i2c/busses build due to I2C_PROC now being gone o i2c: clean up previous w83781d patch due to changes I made to i2c core and build o i2c: remove all proc code from the i2c core, as it's no longer needed o i2c: move i2c-proc to i2c-sensor and clean up all usages of it o i2c: remove unused paramater in found_proc callback function o i2c: remove proc and sysctl code from i2c-proc as it is no longer used o i2c: remove sysctl and proc functions from via686a.c driver o i2c: convert adm1021 chip driver to use sysfs files o i2c: convert lm75 chip driver to use sysfs files o i2c: change the way i2c creates the bus ids to actually be unique now o i2c: fix memleak caused by my last patch fo the adv7175.c driver ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:14 [BK PATCH] More i2c driver changes for 2.5.66 Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.1, 2003/03/25 17:22:33-08:00, j.dittmer@portrix.net [PATCH] i2c: fix compile bugs in tvmixer.c Additionally I need the following patch to tvmixer.c to compile properly with CONFIG_SOUND_TVMIXER=m. Just a /client->name/client->dev.name/g. drivers/media/video/tvmixer.c | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff -Nru a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c --- a/drivers/media/video/tvmixer.c Wed Apr 2 16:02:10 2003 +++ b/drivers/media/video/tvmixer.c Wed Apr 2 16:02:10 2003 @@ -87,7 +87,7 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "tv card", sizeof(info.id)); - strncpy(info.name, client->name, sizeof(info.name)); + strncpy(info.name, client->dev.name, sizeof(info.name)); info.modify_counter = 42 /* FIXME */; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -96,7 +96,7 @@ if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "tv card", sizeof(info.id)); - strncpy(info.name, client->name, sizeof(info.name)); + strncpy(info.name, client->dev.name, sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -237,7 +237,7 @@ int i; if (debug) - printk("tvmixer: adapter %s\n",adap->name); + printk("tvmixer: adapter %s\n",adap->dev.name); for (i=0; i<I2C_CLIENT_MAX; i++) { if (!adap->clients[i]) continue; @@ -261,10 +261,10 @@ /* ignore that one */ if (debug) printk("tvmixer: %s is not a tv card\n", - client->adapter->name); + client->adapter->dev.name); return -1; } - printk("tvmixer: debug: %s\n",client->name); + printk("tvmixer: debug: %s\n",client->dev.name); /* unregister ?? */ for (i = 0; i < DEV_MAX; i++) { @@ -273,7 +273,7 @@ unregister_sound_mixer(devices[i].minor); devices[i].dev = NULL; devices[i].minor = -1; - printk("tvmixer: %s unregistered (#1)\n",client->name); + printk("tvmixer: %s unregistered (#1)\n",client->dev.name); return 0; } } @@ -298,13 +298,13 @@ if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) { if (debug) printk("tvmixer: %s: VIDIOCGAUDIO failed\n", - client->name); + client->dev.name); return -1; } if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) { if (debug) printk("tvmixer: %s: has no volume control\n", - client->name); + client->dev.name); return -1; } @@ -318,7 +318,7 @@ devices[i].count = 0; devices[i].dev = client; printk("tvmixer: %s (%s) registered with minor %d\n", - client->name,client->adapter->name,minor); + client->dev.name,client->adapter->dev.name,minor); return 0; } @@ -344,7 +344,7 @@ if (devices[i].minor != -1) { unregister_sound_mixer(devices[i].minor); printk("tvmixer: %s unregistered (#2)\n", - devices[i].dev->name); + devices[i].dev->dev.name); } } } ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` [PATCH] " Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.2, 2003/03/25 17:29:08-08:00, j.dittmer@portrix.net [PATCH] i2c: add i2c-via686a driver drivers/i2c/chips/Kconfig | 13 drivers/i2c/chips/Makefile | 1 drivers/i2c/chips/via686a.c | 952 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 966 insertions(+) diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig Wed Apr 2 16:02:02 2003 +++ b/drivers/i2c/chips/Kconfig Wed Apr 2 16:02:02 2003 @@ -37,4 +37,17 @@ in the lm_sensors package, which you can download at http://www.lm-sensors.nu +config SENSORS_VIA686A + tristate " VIA686A" + depends on I2C && I2C_PROC + help + support for via686a + If you say yes here you get support for the integrated sensors in + Via 686A/B South Bridges. This can also be built as a module + which can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + endmenu diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile --- a/drivers/i2c/chips/Makefile Wed Apr 2 16:02:02 2003 +++ b/drivers/i2c/chips/Makefile Wed Apr 2 16:02:02 2003 @@ -4,3 +4,4 @@ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_LM75) += lm75.o +obj-$(CONFIG_SENSORS_VIA686A) += via686a.o diff -Nru a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/chips/via686a.c Wed Apr 2 16:02:02 2003 @@ -0,0 +1,952 @@ +/* + via686a.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, + Kyösti Mälkki <kmalkki@cc.hut.fi>, + Mark Studebaker <mdsxyz123@yahoo.com>, + and Bob Dougherty <bobd@stanford.edu> + (Some conversion-factor data were contributed by Jonathan Teh Soon Yew + <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports the Via VT82C686A, VT82C686B south bridges. + Reports all as a 686A. + See doc/chips/via686a for details. + Warning - only supports a single device. +*/ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/i2c-proc.h> +#include <linux/init.h> +#include <asm/io.h> + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(via686a); + +/* + The Via 686a southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many VIA686A constants specified below */ + +/* Length of ISA address segment */ +#define VIA686A_EXTENT 0x80 +#define VIA686A_BASE_REG 0x70 +#define VIA686A_ENABLE_REG 0x74 + +/* The VIA686A registers */ +/* ins numbered 0-4 */ +#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define VIA686A_REG_IN(nr) (0x22 + (nr)) + +/* fans numbered 1-2 */ +#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) +#define VIA686A_REG_FAN(nr) (0x28 + (nr)) + +/* the following values are as speced by VIA: */ +static const u8 regtemp[] = { 0x20, 0x21, 0x1f }; +static const u8 regover[] = { 0x39, 0x3d, 0x1d }; +static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e }; + +/* temps numbered 1-3 */ +#define VIA686A_REG_TEMP(nr) (regtemp[(nr) - 1]) +#define VIA686A_REG_TEMP_OVER(nr) (regover[(nr) - 1]) +#define VIA686A_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) +#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6 +#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6 + +#define VIA686A_REG_ALARM1 0x41 +#define VIA686A_REG_ALARM2 0x42 +#define VIA686A_REG_FANDIV 0x47 +#define VIA686A_REG_CONFIG 0x40 +/* The following register sets temp interrupt mode (bits 1-0 for temp1, + 3-2 for temp2, 5-4 for temp3). Modes are: + 00 interrupt stays as long as value is out-of-range + 01 interrupt is cleared once register is read (default) + 10 comparator mode- like 00, but ignores hysteresis + 11 same as 00 */ +#define VIA686A_REG_TEMP_MODE 0x4b +/* We'll just assume that you want to set all 3 simultaneously: */ +#define VIA686A_TEMP_MODE_MASK 0x3F +#define VIA686A_TEMP_MODE_CONTINUOUS (0x00) + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. + +********* VOLTAGE CONVERSIONS (Bob Dougherty) ******** + From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): + voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp + voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V + voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V + voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V + voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V + in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; + That is: + volts = (25*regVal+133)*factor + regVal = (volts/factor-133)/25 + (These conversions were contributed by Jonathan Teh Soon Yew + <j.teh@iname.com>) + + These get us close, but they don't completely agree with what my BIOS + says- they are all a bit low. But, it all we have to go on... */ +static inline u8 IN_TO_REG(long val, int inNum) +{ + /* to avoid floating point, we multiply everything by 100. + val is guaranteed to be positive, so we can achieve the effect of + rounding by (...*10+5)/10. Note that the *10 is hidden in the + /250 (which should really be /2500). + At the end, we need to /100 because we *100 everything and we need + to /10 because of the rounding thing, so we /1000. */ + if (inNum <= 1) + return (u8) + SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 2) + return (u8) + SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 3) + return (u8) + SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000, + 0, 255); + else + return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5) + / 1000, 0, 255); +} + +static inline long IN_FROM_REG(u8 val, int inNum) +{ + /* to avoid floating point, we multiply everything by 100. + val is guaranteed to be positive, so we can achieve the effect of + rounding by adding 0.5. Or, to avoid fp math, we do (...*10+5)/10. + We need to scale with *100 anyway, so no need to /100 at the end. */ + if (inNum <= 1) + return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10); + else if (inNum == 2) + return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10); + else if (inNum == 3) + return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10); + else + return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10); +} + +/********* FAN RPM CONVERSIONS ********/ +/* Higher register values = slower fans (the fan's strobe gates a counter). + But this chip saturates back at 0, not at 255 like all the other chips. + So, 0 means 0 RPM */ +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 0; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); +} + +#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) + +/******** TEMP CONVERSIONS (Bob Dougherty) *********/ +/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) + if(temp<169) + return double(temp)*0.427-32.08; + else if(temp>=169 && temp<=202) + return double(temp)*0.582-58.16; + else + return double(temp)*0.924-127.33; + + A fifth-order polynomial fits the unofficial data (provided by Alex van + Kaam <darkside@chello.nl>) a bit better. It also give more reasonable + numbers on my machine (ie. they agree with what my BIOS tells me). + Here's the fifth-order fit to the 8-bit data: + temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - + 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. + + (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for + finding my typos in this formula!) + + Alas, none of the elegant function-fit solutions will work because we + aren't allowed to use floating point in the kernel and doing it with + integers doesn't rpovide enough precision. So we'll do boring old + look-up table stuff. The unofficial data (see below) have effectively + 7-bit resolution (they are rounded to the nearest degree). I'm assuming + that the transfer function of the device is monotonic and smooth, so a + smooth function fit to the data will allow us to get better precision. + I used the 5th-order poly fit described above and solved for + VIA register values 0-255. I *10 before rounding, so we get tenth-degree + precision. (I could have done all 1024 values for our 10-bit readings, + but the function is very linear in the useful range (0-80 deg C), so + we'll just use linear interpolation for 10-bit readings.) So, tempLUT + is the temp at via register values 0-255: */ +static const long tempLUT[] = + { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, + -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, + -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, + -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, + -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, + -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, + -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, + 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, + 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, + 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, + 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, + 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, + 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, + 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, + 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, + 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, + 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, + 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, + 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, + 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, + 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, + 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 +}; + +/* the original LUT values from Alex van Kaam <darkside@chello.nl> + (for via register values 12-240): +{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, +-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, +-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, +-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, +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, +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, +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, +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, +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, +85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; + + + Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed + an extra term for a good fit to these inverse data!) and then + solving for each temp value from -50 to 110 (the useable range for + this chip). Here's the fit: + viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 + - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) + Note that n=161: */ +static const u8 viaLUT[] = + { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, + 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, + 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, + 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, + 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, + 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, + 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, + 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, + 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, + 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, + 239, 240 +}; + +/* Converting temps to (8-bit) hyst and over registers + No interpolation here. Just check the limits and go. + The +5 effectively rounds off properly and the +50 is because + the temps start at -50 */ +static inline u8 TEMP_TO_REG(long val) +{ + return (u8) + SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 : + ((val + 5) / 10 + 50))], 0, 255); +} + +/* for 8-bit temperature hyst and over registers + The temp values are already *10, so we don't need to do that. + But we _will_ round these off to the nearest degree with (...*10+5)/10 */ +#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10) + +/* for 10-bit temperature readings + You might _think_ this is too long to inline, but's it's really only + called once... */ +static inline long TEMP_FROM_REG10(u16 val) +{ + /* the temp values are already *10, so we don't need to do that. */ + long temp; + u16 eightBits = val >> 2; + u16 twoBits = val & 3; + + /* handle the extremes first (they won't interpolate well! ;-) */ + if (val == 0) + return (long) tempLUT[0]; + if (val == 1023) + return (long) tempLUT[255]; + + if (twoBits == 0) + return (long) tempLUT[eightBits]; + else { + /* do some interpolation by multipying the lower and upper + bounds by 25, 50 or 75, then /100. */ + temp = ((25 * (4 - twoBits)) * tempLUT[eightBits] + + (25 * twoBits) * tempLUT[eightBits + 1]); + /* increase the magnitude by 50 to achieve rounding. */ + if (temp > 0) + temp += 50; + else + temp -= 50; + return (temp / 100); + } +} + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits */ +#define VIA686A_INIT_IN_0 200 +#define VIA686A_INIT_IN_1 250 +#define VIA686A_INIT_IN_2 330 +#define VIA686A_INIT_IN_3 500 +#define VIA686A_INIT_IN_4 1200 + +#define VIA686A_INIT_IN_PERCENTAGE 10 + +#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) + +#define VIA686A_INIT_FAN_MIN 3000 + +#define VIA686A_INIT_TEMP_OVER 600 +#define VIA686A_INIT_TEMP_HYST 500 + +/* For the VIA686A, we need to keep some data in memory. That + data is pointed to by via686a_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new via686a client is + allocated. */ +struct via686a_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u16 temp[3]; /* Register value 10 bit */ + u8 temp_over[3]; /* Register value */ + u8 temp_hyst[3]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ + +static int via686a_attach_adapter(struct i2c_adapter *adapter); +static int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int via686a_detach_client(struct i2c_client *client); + +static int via686a_read_value(struct i2c_client *client, u8 register); +static void via686a_write_value(struct i2c_client *client, u8 register, + u8 value); +static void via686a_update_client(struct i2c_client *client); +static void via686a_init_client(struct i2c_client *client); + + +static void via686a_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int via686a_id = 0; + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver via686a_driver = { + .owner = THIS_MODULE, + .name = "VIA686A", + .id = I2C_DRIVERID_VIA686A, + .flags = I2C_DF_NOTIFY, + .attach_adapter = via686a_attach_adapter, + .detach_client = via686a_detach_client, +}; + + + +/* The /proc/sys entries */ + +/* -- SENSORS SYSCTL START -- */ +#define VIA686A_SYSCTL_IN0 1000 +#define VIA686A_SYSCTL_IN1 1001 +#define VIA686A_SYSCTL_IN2 1002 +#define VIA686A_SYSCTL_IN3 1003 +#define VIA686A_SYSCTL_IN4 1004 +#define VIA686A_SYSCTL_FAN1 1101 +#define VIA686A_SYSCTL_FAN2 1102 +#define VIA686A_SYSCTL_TEMP 1200 +#define VIA686A_SYSCTL_TEMP2 1201 +#define VIA686A_SYSCTL_TEMP3 1202 +#define VIA686A_SYSCTL_FAN_DIV 2000 +#define VIA686A_SYSCTL_ALARMS 2001 + +#define VIA686A_ALARM_IN0 0x01 +#define VIA686A_ALARM_IN1 0x02 +#define VIA686A_ALARM_IN2 0x04 +#define VIA686A_ALARM_IN3 0x08 +#define VIA686A_ALARM_TEMP 0x10 +#define VIA686A_ALARM_FAN1 0x40 +#define VIA686A_ALARM_FAN2 0x80 +#define VIA686A_ALARM_IN4 0x100 +#define VIA686A_ALARM_TEMP2 0x800 +#define VIA686A_ALARM_CHAS 0x1000 +#define VIA686A_ALARM_TEMP3 0x8000 + +/* -- SENSORS SYSCTL END -- */ + +/* These files are created for each detected VIA686A. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table via686a_dir_table_template[] = { + {VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div}, + {VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms}, + {0} +}; + +static inline int via686a_read_value(struct i2c_client *client, u8 reg) +{ + return (inb_p(client->addr + reg)); +} + +static inline void via686a_write_value(struct i2c_client *client, u8 reg, + u8 value) +{ + outb_p(value, client->addr + reg); +} + +/* This is called when the module is loaded */ +static int via686a_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, via686a_detect); +} + +int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct via686a_data *data; + int err = 0; + const char *type_name = "via686a"; + const char client_name[] = "via686a chip"; + u16 val; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + dev_err(&adapter->dev, + "via686a_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + /* 8231 requires multiple of 256, we enforce that on 686 as well */ + if(force_addr) + address = force_addr & 0xFF00; + + if(force_addr) { + dev_warn(&adapter->dev,"forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) + return -ENODEV; + } + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) + return -ENODEV; + if (!(val & 0x0001)) { + dev_warn(&adapter->dev,"enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, + val | 0x0001)) + return -ENODEV; + } + + /* Reserve the ISA region */ + if (!request_region(address, VIA686A_EXTENT, "via686a-sensor")) { + dev_err(&adapter->dev,"region 0x%x already in use!\n", + address); + return -ENODEV; + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct via686a_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + memset(new_client,0x00, sizeof(struct i2c_client) + + sizeof(struct via686a_data)); + data = (struct via686a_data *) (new_client + 1); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &via686a_driver; + new_client->flags = 0; + + /* Fill in the remaining client fields and put into the global list */ + snprintf(new_client->dev.name, DEVICE_NAME_SIZE, client_name); + + new_client->id = via686a_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + via686a_dir_table_template)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the VIA686A chip */ + via686a_init_client(new_client); + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + release_region(address, VIA686A_EXTENT); + kfree(new_client); + ERROR0: + return err; +} + +static int via686a_detach_client(struct i2c_client *client) +{ + int err; + struct via686a_data *data = i2c_get_clientdata(client); + i2c_deregister_entry(data->sysctl_id); + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, VIA686A_EXTENT); + kfree(client); + + return 0; +} + +/* Called when we have found a new VIA686A. Set limits, etc. */ +static void via686a_init_client(struct i2c_client *client) +{ + int i; + + /* Reset the device */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x80); + + /* Have to wait for reset to complete or else the following + initializations won't work reliably. The delay was arrived at + empirically, the datasheet doesn't tell you. + Waiting for the reset bit to clear doesn't work, it + clears in about 2-4 udelays and that isn't nearly enough. */ + udelay(50); + + via686a_write_value(client, VIA686A_REG_IN_MIN(0), + IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MAX(0), + IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MIN(1), + IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MAX(1), + IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MIN(2), + IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MAX(2), + IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MIN(3), + IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MAX(3), + IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MIN(4), + IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4)); + via686a_write_value(client, VIA686A_REG_IN_MAX(4), + IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(1), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(2), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + for (i = 1; i <= 3; i++) { + via686a_write_value(client, VIA686A_REG_TEMP_OVER(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_OVER)); + via686a_write_value(client, VIA686A_REG_TEMP_HYST(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_HYST)); + } + + /* Start monitoring */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x01); + + /* Cofigure temp interrupt mode for continuous-interrupt operation */ + via686a_write_value(client, VIA686A_REG_TEMP_MODE, + via686a_read_value(client, VIA686A_REG_TEMP_MODE) & + !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); +} + +static void via686a_update_client(struct i2c_client *client) +{ + struct via686a_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(client, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(client, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(client, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(client, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(client, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + data->temp[i - 1] = via686a_read_value(client, + VIA686A_REG_TEMP(i)) << 2; + data->temp_over[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_OVER(i)); + data->temp_hyst[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_HYST(i)); + } + /* add in lower 2 bits + temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(client, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + i = via686a_read_value(client, VIA686A_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + via686a_read_value(client, + VIA686A_REG_ALARM1) | + (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +static void via686a_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = i2c_get_clientdata(client); + int nr = ctl_name - VIA686A_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr], nr); + results[1] = IN_FROM_REG(data->in_max[nr], nr); + results[2] = IN_FROM_REG(data->in[nr], nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0], nr); + via686a_write_value(client, VIA686A_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1], nr); + via686a_write_value(client, VIA686A_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void via686a_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = i2c_get_clientdata(client); + int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div + [nr - 1])); + results[1] = FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div[nr -1])); + via686a_write_value(client, + VIA686A_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + +void via686a_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = i2c_get_clientdata(client); + int nr = ctl_name - VIA686A_SYSCTL_TEMP; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over[nr]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); + results[2] = TEMP_FROM_REG10(data->temp[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over[nr] = TEMP_TO_REG(results[0]); + via686a_write_value(client, + VIA686A_REG_TEMP_OVER(nr + 1), + data->temp_over[nr]); + } + if (*nrels_mag >= 2) { + data->temp_hyst[nr] = TEMP_TO_REG(results[1]); + via686a_write_value(client, + VIA686A_REG_TEMP_HYST(nr + 1), + data->temp_hyst[nr]); + } + } +} + +void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = i2c_get_clientdata(client); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct via686a_data *data = i2c_get_clientdata(client); + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = via686a_read_value(client, VIA686A_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + via686a_write_value(client, VIA686A_REG_FANDIV, + old); + } + } +} + + +static struct pci_device_id via686a_pci_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_VIA, + .device = PCI_DEVICE_ID_VIA_82C686_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0, + .class_mask = 0, + .driver_data = 0, + }, + { 0, } +}; + +static int __devinit via686a_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 val; + int addr = 0; + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(dev, VIA686A_BASE_REG, &val)) + return -ENODEV; + + addr = val & ~(VIA686A_EXTENT - 1); + if (addr == 0 && force_addr == 0) { + dev_err(&dev->dev,"base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + addr = force_addr; /* so detect will get called */ + + if (!addr) { + dev_err(&dev->dev,"No Via 686A sensors found.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + s_bridge = dev; + return i2c_add_driver(&via686a_driver); +} + +static void __devexit via686a_pci_remove(struct pci_dev *dev) +{ + i2c_del_driver(&via686a_driver); +} + +static struct pci_driver via686a_pci_driver = { + .name = "via686a", + .id_table = via686a_pci_ids, + .probe = via686a_pci_probe, + .remove = __devexit_p(via686a_pci_remove), +}; + +static int __init sm_via686a_init(void) +{ + return pci_module_init(&via686a_pci_driver); +} + +static void __exit sm_via686a_exit(void) +{ + pci_unregister_driver(&via686a_pci_driver); +} + +MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, " + "Mark Studebaker <mdsxyz123@yahoo.com> " + "and Bob Dougherty <bobd@stanford.edu>"); +MODULE_DESCRIPTION("VIA 686A Sensor device"); +MODULE_LICENSE("GPL"); + +module_init(sm_via686a_init); +module_exit(sm_via686a_exit); ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.3, 2003/03/31 15:25:48-08:00, greg@kroah.com [PATCH] i2c: fix memleak caused by my last patch fo the adv7175.c driver drivers/media/video/adv7175.c | 2 +- 1 files changed, 1 insertion(+), 1 deletion(-) diff -Nru a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c --- a/drivers/media/video/adv7175.c Wed Apr 2 16:01:53 2003 +++ b/drivers/media/video/adv7175.c Wed Apr 2 16:01:53 2003 @@ -231,7 +231,7 @@ static int adv717x_detach(struct i2c_client *client) { i2c_detach_client(client); - i2c_get_clientdata(client); + kfree(i2c_get_clientdata(client)); kfree(client); return 0; } ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.4, 2003/04/01 11:48:21-08:00, greg@kroah.com [PATCH] i2c: change the way i2c creates the bus ids to actually be unique now. It also is much like the old naming scheme, to keep things consistent. drivers/i2c/i2c-core.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletion(-) diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c Wed Apr 2 16:01:44 2003 +++ b/drivers/i2c/i2c-core.c Wed Apr 2 16:01:44 2003 @@ -392,7 +392,8 @@ client->dev.driver = &client->driver->driver; client->dev.bus = &i2c_bus_type; - snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), "i2c_dev_%d", i); + snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), + "%d-%04x", i2c_adapter_id(adapter), client->addr); printk("registering %s\n", client->dev.bus_id); device_register(&client->dev); ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.5, 2003/04/02 11:29:55-08:00, greg@kroah.com i2c: convert lm75 chip driver to use sysfs files. drivers/i2c/chips/lm75.c | 185 +++++++++++++++++++---------------------------- 1 files changed, 77 insertions(+), 108 deletions(-) diff -Nru a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c --- a/drivers/i2c/chips/lm75.c Wed Apr 2 16:01:36 2003 +++ b/drivers/i2c/chips/lm75.c Wed Apr 2 16:01:36 2003 @@ -18,6 +18,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* #define DEBUG 1 */ + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -25,8 +27,6 @@ #include <linux/i2c-proc.h> -#define LM75_SYSCTL_TEMP 1200 /* Degrees Celsius * 10 */ - /* Addresses to scan */ static unsigned short normal_i2c[] = { SENSORS_I2C_END }; static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; @@ -39,97 +39,113 @@ /* Many LM75 constants specified below */ /* The LM75 registers */ -#define LM75_REG_TEMP 0x00 -#define LM75_REG_CONF 0x01 -#define LM75_REG_TEMP_HYST 0x02 -#define LM75_REG_TEMP_OS 0x03 +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_TEMP_HYST 0x02 +#define LM75_REG_TEMP_OS 0x03 /* Conversions. Rounding and limit checking is only done on the TO_REG variants. Note that you should be a bit careful with which arguments these macros are called: arguments may be evaluated more than once. Fixing this is just not worth it. */ -#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) -#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) +#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) /* Initial values */ -#define LM75_INIT_TEMP_OS 600 -#define LM75_INIT_TEMP_HYST 500 +#define LM75_INIT_TEMP_OS 600 +#define LM75_INIT_TEMP_HYST 500 /* Each client has this additional data */ struct lm75_data { - int sysctl_id; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u16 temp, temp_os, temp_hyst; /* Register values */ + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u16 temp_input; /* Register values */ + u16 temp_max; + u16 temp_hyst; }; static int lm75_attach_adapter(struct i2c_adapter *adapter); -static int lm75_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind); +static int lm75_detect(struct i2c_adapter *adapter, int address, int kind); static void lm75_init_client(struct i2c_client *client); static int lm75_detach_client(struct i2c_client *client); -static u16 swap_bytes(u16 val); static int lm75_read_value(struct i2c_client *client, u8 reg); static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); -static void lm75_temp(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); static void lm75_update_client(struct i2c_client *client); /* This is the driver that will be inserted */ static struct i2c_driver lm75_driver = { .owner = THIS_MODULE, - .name = "LM75 sensor", + .name = "lm75", .id = I2C_DRIVERID_LM75, .flags = I2C_DF_NOTIFY, .attach_adapter = lm75_attach_adapter, .detach_client = lm75_detach_client, }; -/* These files are created for each detected LM75. This is just a template; - though at first sight, you might think we could use a statically - allocated list, we need some way to get back to the parent - which - is done through one of the 'extra' fields which are initialized - when a new copy is allocated. */ -static ctl_table lm75_dir_table_template[] = { - {LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &lm75_temp}, - {0} -}; - static int lm75_id = 0; +#define show(value) \ +static ssize_t show_##value(struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm75_data *data = i2c_get_clientdata(client); \ + int temp; \ + \ + lm75_update_client(client); \ + temp = TEMP_FROM_REG(data->value); \ + return sprintf(buf, "%d\n", temp * 100); \ +} +show(temp_max); +show(temp_hyst); +show(temp_input); + +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm75_data *data = i2c_get_clientdata(client); \ + int temp = simple_strtoul(buf, NULL, 10) / 100; \ + \ + data->value = TEMP_TO_REG(temp); \ + lm75_write_value(client, reg, data->value); \ + return count; \ +} +set(temp_max, LM75_REG_TEMP_OS); +set(temp_hyst, LM75_REG_TEMP_HYST); + +static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); +static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); +static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL); + static int lm75_attach_adapter(struct i2c_adapter *adapter) { return i2c_detect(adapter, &addr_data, lm75_detect); } /* This function is called by i2c_detect */ -static int lm75_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind) +static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) { int i, cur, conf, hyst, os; struct i2c_client *new_client; struct lm75_data *data; int err = 0; - const char *type_name, *client_name; + const char *name; /* Make sure we aren't probing the ISA bus!! This is just a safety check at this moment; i2c_detect really won't call us. */ #ifdef DEBUG if (i2c_is_isa_adapter(adapter)) { - printk - ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n"); - return 0; + dev_dbg(&adapter->dev, + "lm75_detect called for an ISA bus adapter?!?\n"); + goto exit; } #endif if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) - goto error0; + goto exit; /* OK. For now, we presume we have a valid client. We now create the client structure, even though we cannot fill it completely yet. @@ -138,7 +154,7 @@ sizeof(struct lm75_data), GFP_KERNEL))) { err = -ENOMEM; - goto error0; + goto exit; } memset(new_client, 0x00, sizeof(struct i2c_client) + sizeof(struct lm75_data)); @@ -157,16 +173,10 @@ hyst = i2c_smbus_read_word_data(new_client, 2); os = i2c_smbus_read_word_data(new_client, 3); for (i = 0; i <= 0x1f; i++) - if ( - (i2c_smbus_read_byte_data - (new_client, i * 8 + 1) != conf) - || - (i2c_smbus_read_word_data - (new_client, i * 8 + 2) != hyst) - || - (i2c_smbus_read_word_data - (new_client, i * 8 + 3) != os)) - goto error1; + if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) || + (i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) || + (i2c_smbus_read_word_data(new_client, i * 8 + 3) != os)) + goto exit_free; } /* Determine the chip type - only one kind supported! */ @@ -174,15 +184,15 @@ kind = lm75; if (kind == lm75) { - type_name = "lm75"; - client_name = "LM75 chip"; + name = "lm75"; } else { - pr_debug("lm75.o: Internal error: unknown kind (%d)?!?", kind); - goto error1; + dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?", + kind); + goto exit_free; } /* Fill in the remaining client fields and put it into the global list */ - strncpy(new_client->dev.name, client_name, DEVICE_NAME_SIZE); + strncpy(new_client->dev.name, name, DEVICE_NAME_SIZE); new_client->id = lm75_id++; data->valid = 0; @@ -190,36 +200,23 @@ /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) - goto error3; + goto exit_free; - /* Register a new directory entry with module sensors */ - i = i2c_register_entry(new_client, type_name, lm75_dir_table_template); - if (i < 0) { - err = i; - goto error4; - } - data->sysctl_id = i; + device_create_file(&new_client->dev, &dev_attr_temp_max); + device_create_file(&new_client->dev, &dev_attr_temp_min); + device_create_file(&new_client->dev, &dev_attr_temp_input); lm75_init_client(new_client); return 0; -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ - - error4: - i2c_detach_client(new_client); - error3: - error1: +exit_free: kfree(new_client); - error0: +exit: return err; } static int lm75_detach_client(struct i2c_client *client) { - struct lm75_data *data = i2c_get_clientdata(client); - - i2c_deregister_entry(data->sysctl_id); i2c_detach_client(client); kfree(client); return 0; @@ -271,44 +268,16 @@ if ((jiffies - data->last_updated > HZ + HZ / 2) || (jiffies < data->last_updated) || !data->valid) { - pr_debug("Starting lm75 update\n"); + dev_dbg(&client->dev, "Starting lm75 update\n"); - data->temp = lm75_read_value(client, LM75_REG_TEMP); - data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS); - data->temp_hyst = - lm75_read_value(client, LM75_REG_TEMP_HYST); + data->temp_input = lm75_read_value(client, LM75_REG_TEMP); + data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS); + data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST); data->last_updated = jiffies; data->valid = 1; } up(&data->update_lock); -} - - -static void lm75_temp(struct i2c_client *client, int operation, int ctl_name, - int *nrels_mag, long *results) -{ - struct lm75_data *data = i2c_get_clientdata(client); - if (operation == SENSORS_PROC_REAL_INFO) - *nrels_mag = 1; - else if (operation == SENSORS_PROC_REAL_READ) { - lm75_update_client(client); - results[0] = TEMP_FROM_REG(data->temp_os); - results[1] = TEMP_FROM_REG(data->temp_hyst); - results[2] = TEMP_FROM_REG(data->temp); - *nrels_mag = 3; - } else if (operation == SENSORS_PROC_REAL_WRITE) { - if (*nrels_mag >= 1) { - data->temp_os = TEMP_TO_REG(results[0]); - lm75_write_value(client, LM75_REG_TEMP_OS, - data->temp_os); - } - if (*nrels_mag >= 2) { - data->temp_hyst = TEMP_TO_REG(results[1]); - lm75_write_value(client, LM75_REG_TEMP_HYST, - data->temp_hyst); - } - } } static int __init sensors_lm75_init(void) ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.6, 2003/04/02 11:32:24-08:00, greg@kroah.com i2c: convert adm1021 chip driver to use sysfs files. Note, some data is not converted and will not be displayed. Someone with this hardware is going to have to finish the rest of this conversion. drivers/i2c/chips/adm1021.c | 358 ++++++++++++++++++++------------------------ 1 files changed, 166 insertions(+), 192 deletions(-) diff -Nru a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c --- a/drivers/i2c/chips/adm1021.c Wed Apr 2 16:01:25 2003 +++ b/drivers/i2c/chips/adm1021.c Wed Apr 2 16:01:25 2003 @@ -53,34 +53,34 @@ /* The adm1021 registers */ /* Read-only */ -#define ADM1021_REG_TEMP 0x00 -#define ADM1021_REG_REMOTE_TEMP 0x01 -#define ADM1021_REG_STATUS 0x02 -#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ -#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ -#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +#define ADM1021_REG_TEMP 0x00 +#define ADM1021_REG_REMOTE_TEMP 0x01 +#define ADM1021_REG_STATUS 0x02 +#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ +#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ /* These use different addresses for reading/writing */ -#define ADM1021_REG_CONFIG_R 0x03 -#define ADM1021_REG_CONFIG_W 0x09 -#define ADM1021_REG_CONV_RATE_R 0x04 -#define ADM1021_REG_CONV_RATE_W 0x0A +#define ADM1021_REG_CONFIG_R 0x03 +#define ADM1021_REG_CONFIG_W 0x09 +#define ADM1021_REG_CONV_RATE_R 0x04 +#define ADM1021_REG_CONV_RATE_W 0x0A /* These are for the ADM1023's additional precision on the remote temp sensor */ -#define ADM1021_REG_REM_TEMP_PREC 0x010 -#define ADM1021_REG_REM_OFFSET 0x011 -#define ADM1021_REG_REM_OFFSET_PREC 0x012 -#define ADM1021_REG_REM_TOS_PREC 0x013 -#define ADM1021_REG_REM_THYST_PREC 0x014 +#define ADM1021_REG_REM_TEMP_PREC 0x010 +#define ADM1021_REG_REM_OFFSET 0x011 +#define ADM1021_REG_REM_OFFSET_PREC 0x012 +#define ADM1021_REG_REM_TOS_PREC 0x013 +#define ADM1021_REG_REM_THYST_PREC 0x014 /* limits */ -#define ADM1021_REG_TOS_R 0x05 -#define ADM1021_REG_TOS_W 0x0B -#define ADM1021_REG_REMOTE_TOS_R 0x07 -#define ADM1021_REG_REMOTE_TOS_W 0x0D -#define ADM1021_REG_THYST_R 0x06 -#define ADM1021_REG_THYST_W 0x0C -#define ADM1021_REG_REMOTE_THYST_R 0x08 -#define ADM1021_REG_REMOTE_THYST_W 0x0E +#define ADM1021_REG_TOS_R 0x05 +#define ADM1021_REG_TOS_W 0x0B +#define ADM1021_REG_REMOTE_TOS_R 0x07 +#define ADM1021_REG_REMOTE_TOS_W 0x0D +#define ADM1021_REG_THYST_R 0x06 +#define ADM1021_REG_THYST_W 0x0C +#define ADM1021_REG_REMOTE_THYST_R 0x08 +#define ADM1021_REG_REMOTE_THYST_W 0x0E /* write-only */ -#define ADM1021_REG_ONESHOT 0x0F +#define ADM1021_REG_ONESHOT 0x0F /* Conversions. Rounding and limit checking is only done on the TO_REG @@ -88,8 +88,8 @@ these macros are called: arguments may be evaluated more than once. Fixing this is just not worth it. */ /* Conversions note: 1021 uses normal integer signed-byte format*/ -#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) -#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) +#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) /* Initial values */ @@ -97,44 +97,43 @@ they don't quite work like a thermostat the way the LM75 does. I.e., a lower temp than THYST actually triggers an alarm instead of clearing it. Weird, ey? --Phil */ -#define adm1021_INIT_TOS 60 -#define adm1021_INIT_THYST 20 -#define adm1021_INIT_REMOTE_TOS 60 -#define adm1021_INIT_REMOTE_THYST 20 +#define adm1021_INIT_TOS 60 +#define adm1021_INIT_THYST 20 +#define adm1021_INIT_REMOTE_TOS 60 +#define adm1021_INIT_REMOTE_THYST 20 /* Each client has this additional data */ struct adm1021_data { - int sysctl_id; enum chips type; struct semaphore update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u8 temp, temp_os, temp_hyst; /* Register values */ - u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code; + u8 temp_max; /* Register values */ + u8 temp_hyst; + u8 temp_input; + u8 remote_temp_max; + u8 remote_temp_hyst; + u8 remote_temp_input; + u8 alarms; + /* special values for ADM1021 only */ + u8 die_code; /* Special values for ADM1023 only */ - u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, - remote_temp_offset, remote_temp_offset_prec; + u8 remote_temp_prec; + u8 remote_temp_os_prec; + u8 remote_temp_hyst_prec; + u8 remote_temp_offset; + u8 remote_temp_offset_prec; }; static int adm1021_attach_adapter(struct i2c_adapter *adapter); -static int adm1021_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind); +static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind); static void adm1021_init_client(struct i2c_client *client); static int adm1021_detach_client(struct i2c_client *client); static int adm1021_read_value(struct i2c_client *client, u8 reg); static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value); -static void adm1021_temp(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void adm1021_remote_temp(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, - long *results); -static void adm1021_alarms(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void adm1021_die_code(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); static void adm1021_update_client(struct i2c_client *client); /* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ @@ -151,45 +150,63 @@ .detach_client = adm1021_detach_client, }; -/* These files are created for each detected adm1021. This is just a template; - though at first sight, you might think we could use a statically - allocated list, we need some way to get back to the parent - which - is done through one of the 'extra' fields which are initialized - when a new copy is allocated. */ -static ctl_table adm1021_dir_table_template[] = { - {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_temp}, - {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_remote_temp}, - {ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_die_code}, - {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_alarms}, - {0} -}; - -static ctl_table adm1021_max_dir_table_template[] = { - {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_temp}, - {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_remote_temp}, - {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, - &i2c_sysctl_real, NULL, &adm1021_alarms}, - {0} -}; - /* I choose here for semi-static allocation. Complete dynamic allocation could also be used; the code needed for this would probably take more memory than the datastructure takes now. */ static int adm1021_id = 0; +#define show(value) \ +static ssize_t show_##value(struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1021_data *data = i2c_get_clientdata(client); \ + int temp; \ + \ + adm1021_update_client(client); \ + temp = TEMP_FROM_REG(data->value); \ + return sprintf(buf, "%d\n", temp); \ +} +show(temp_max); +show(temp_hyst); +show(temp_input); +show(remote_temp_max); +show(remote_temp_hyst); +show(remote_temp_input); +show(alarms); +show(die_code); + +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1021_data *data = i2c_get_clientdata(client); \ + int temp = simple_strtoul(buf, NULL, 10); \ + \ + data->value = TEMP_TO_REG(temp); \ + adm1021_write_value(client, reg, data->value); \ + return count; \ +} +set(temp_max, ADM1021_REG_TOS_W); +set(temp_hyst, ADM1021_REG_THYST_W); +set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W); +set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W); + +static DEVICE_ATTR(temp_max1, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); +static DEVICE_ATTR(temp_min1, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); +static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input, NULL); +static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max); +static DEVICE_ATTR(temp_min2, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst); +static DEVICE_ATTR(temp_input2, S_IRUGO, show_remote_temp_input, NULL); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(die_code, S_IRUGO, show_die_code, NULL); + + static int adm1021_attach_adapter(struct i2c_adapter *adapter) { return i2c_detect(adapter, &addr_data, adm1021_detect); } -static int adm1021_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind) +static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) { int i; struct i2c_client *new_client; @@ -202,8 +219,7 @@ at this moment; i2c_detect really won't call us. */ #ifdef DEBUG if (i2c_is_isa_adapter(adapter)) { - printk - ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n"); + dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n"); return 0; } #endif @@ -232,35 +248,28 @@ new_client->flags = 0; /* Now, we do the remaining detection. */ - if (kind < 0) { - if ( - (adm1021_read_value(new_client, ADM1021_REG_STATUS) & - 0x03) != 0x00) + if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00) goto error1; } /* Determine the chip type. */ - if (kind <= 0) { i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); if (i == 0x41) - if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) - kind = adm1023; - else - kind = adm1021; + if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) + kind = adm1023; + else + kind = adm1021; else if (i == 0x49) kind = thmc10; else if (i == 0x23) kind = gl523sm; else if ((i == 0x4d) && - (adm1021_read_value - (new_client, ADM1021_REG_DEV_ID) == 0x01)) + (adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01)) kind = max1617a; /* LM84 Mfr ID in a different place */ - else - if (adm1021_read_value - (new_client, ADM1021_REG_CONV_RATE_R) == 0x00) + else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00) kind = lm84; else if (i == 0x54) kind = mc1066; @@ -293,10 +302,8 @@ type_name = "mc1066"; client_name = "MC1066 chip"; } else { -#ifdef DEBUG - printk("adm1021.o: Internal error: unknown kind (%d)?!?", - kind); -#endif + dev_err(&adapter->dev, "Internal error: unknown kind (%d)?!?", + kind); goto error1; } @@ -312,25 +319,24 @@ if ((err = i2c_attach_client(new_client))) goto error3; - /* Register a new directory entry with module sensors */ - err = i2c_register_entry(new_client, type_name, - (data->type == adm1021) ? - adm1021_dir_table_template : - adm1021_max_dir_table_template); - if (err < 0) - goto error4; + device_create_file(&new_client->dev, &dev_attr_temp_max1); + device_create_file(&new_client->dev, &dev_attr_temp_min1); + device_create_file(&new_client->dev, &dev_attr_temp_input1); + device_create_file(&new_client->dev, &dev_attr_temp_max2); + device_create_file(&new_client->dev, &dev_attr_temp_min2); + device_create_file(&new_client->dev, &dev_attr_temp_input2); + device_create_file(&new_client->dev, &dev_attr_alarms); + if (data->type == adm1021) + device_create_file(&new_client->dev, &dev_attr_die_code); - data->sysctl_id = err; /* Initialize the ADM1021 chip */ adm1021_init_client(new_client); return 0; - error4: - i2c_detach_client(new_client); - error3: - error1: +error3: +error1: kfree(new_client); - error0: +error0: return err; } @@ -353,21 +359,15 @@ static int adm1021_detach_client(struct i2c_client *client) { - int err; - i2c_deregister_entry(((struct adm1021_data *) (i2c_get_clientdata(client)))->sysctl_id); - if ((err = i2c_detach_client(client))) { - printk - ("adm1021.o: Client deregistration failed, client not detached.\n"); + dev_err(&client->dev, "Client deregistration failed, client not detached.\n"); return err; } kfree(client); - return 0; - } /* All registers are byte-sized */ @@ -391,39 +391,23 @@ if ((jiffies - data->last_updated > HZ + HZ / 2) || (jiffies < data->last_updated) || !data->valid) { + dev_dbg(&client->dev, "Starting adm1021 update\n"); -#ifdef DEBUG - printk("Starting adm1021 update\n"); -#endif - - data->temp = adm1021_read_value(client, ADM1021_REG_TEMP); - data->temp_os = - adm1021_read_value(client, ADM1021_REG_TOS_R); - data->temp_hyst = - adm1021_read_value(client, ADM1021_REG_THYST_R); - data->remote_temp = - adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); - data->remote_temp_os = - adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); - data->remote_temp_hyst = - adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); - data->alarms = - adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec; + data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP); + data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R); + data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R); + data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); + data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); + data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); + data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec; if (data->type == adm1021) - data->die_code = - adm1021_read_value(client, - ADM1021_REG_DIE_CODE); + data->die_code = adm1021_read_value(client, ADM1021_REG_DIE_CODE); if (data->type == adm1023) { - data->remote_temp_prec = - adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); - data->remote_temp_os_prec = - adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); - data->remote_temp_hyst_prec = - adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); - data->remote_temp_offset = - adm1021_read_value(client, ADM1021_REG_REM_OFFSET); - data->remote_temp_offset_prec = - adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); + data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); + data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); + data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); + data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET); + data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); } data->last_updated = jiffies; data->valid = 1; @@ -433,6 +417,9 @@ } +/* FIXME, remove these four functions, they are here to verify the sysfs + * conversion is correct, or not */ +__attribute__((unused)) static void adm1021_temp(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { @@ -442,15 +429,15 @@ *nrels_mag = 0; else if (operation == SENSORS_PROC_REAL_READ) { adm1021_update_client(client); - results[0] = TEMP_FROM_REG(data->temp_os); + results[0] = TEMP_FROM_REG(data->temp_max); results[1] = TEMP_FROM_REG(data->temp_hyst); - results[2] = TEMP_FROM_REG(data->temp); + results[2] = TEMP_FROM_REG(data->temp_input); *nrels_mag = 3; } else if (operation == SENSORS_PROC_REAL_WRITE) { if (*nrels_mag >= 1) { - data->temp_os = TEMP_TO_REG(results[0]); + data->temp_max = TEMP_TO_REG(results[0]); adm1021_write_value(client, ADM1021_REG_TOS_W, - data->temp_os); + data->temp_max); } if (*nrels_mag >= 2) { data->temp_hyst = TEMP_TO_REG(results[1]); @@ -460,6 +447,7 @@ } } +__attribute__((unused)) static void adm1021_remote_temp(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { @@ -471,68 +459,53 @@ else { *nrels_mag = 0; } else if (operation == SENSORS_PROC_REAL_READ) { adm1021_update_client(client); - results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[0] = TEMP_FROM_REG(data->remote_temp_max); results[1] = TEMP_FROM_REG(data->remote_temp_hyst); - results[2] = TEMP_FROM_REG(data->remote_temp); + results[2] = TEMP_FROM_REG(data->remote_temp_input); if (data->type == adm1023) { - results[0]=results[0]*1000 + - ((data->remote_temp_os_prec >> 5) * 125); - results[1]=results[1]*1000 + - ((data->remote_temp_hyst_prec >> 5) * 125); - results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + - ((data->remote_temp_offset_prec >> 5) * 125); - results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + - ((data->remote_temp_prec >> 5) * 125); - *nrels_mag = 4; + results[0] = results[0]*1000 + ((data->remote_temp_os_prec >> 5) * 125); + results[1] = results[1]*1000 + ((data->remote_temp_hyst_prec >> 5) * 125); + results[2] = (TEMP_FROM_REG(data->remote_temp_offset)*1000) + ((data->remote_temp_offset_prec >> 5) * 125); + results[3] = (TEMP_FROM_REG(data->remote_temp_input)*1000) + ((data->remote_temp_prec >> 5) * 125); + *nrels_mag = 4; } else { - *nrels_mag = 3; + *nrels_mag = 3; } } else if (operation == SENSORS_PROC_REAL_WRITE) { if (*nrels_mag >= 1) { if (data->type == adm1023) { - prec=((results[0]-((results[0]/1000)*1000))/125)<<5; - adm1021_write_value(client, - ADM1021_REG_REM_TOS_PREC, - prec); - results[0]=results[0]/1000; - data->remote_temp_os_prec=prec; + prec = ((results[0]-((results[0]/1000)*1000))/125)<<5; + adm1021_write_value(client, ADM1021_REG_REM_TOS_PREC, prec); + results[0] = results[0]/1000; + data->remote_temp_os_prec=prec; } - data->remote_temp_os = TEMP_TO_REG(results[0]); - adm1021_write_value(client, - ADM1021_REG_REMOTE_TOS_W, - data->remote_temp_os); + data->remote_temp_max = TEMP_TO_REG(results[0]); + adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W, data->remote_temp_max); } if (*nrels_mag >= 2) { if (data->type == adm1023) { - prec=((results[1]-((results[1]/1000)*1000))/125)<<5; - adm1021_write_value(client, - ADM1021_REG_REM_THYST_PREC, - prec); - results[1]=results[1]/1000; - data->remote_temp_hyst_prec=prec; + prec = ((results[1]-((results[1]/1000)*1000))/125)<<5; + adm1021_write_value(client, ADM1021_REG_REM_THYST_PREC, prec); + results[1] = results[1]/1000; + data->remote_temp_hyst_prec=prec; } data->remote_temp_hyst = TEMP_TO_REG(results[1]); - adm1021_write_value(client, - ADM1021_REG_REMOTE_THYST_W, - data->remote_temp_hyst); + adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W, data->remote_temp_hyst); } if (*nrels_mag >= 3) { if (data->type == adm1023) { - prec=((results[2]-((results[2]/1000)*1000))/125)<<5; - adm1021_write_value(client, - ADM1021_REG_REM_OFFSET_PREC, - prec); - results[2]=results[2]/1000; - data->remote_temp_offset_prec=prec; - data->remote_temp_offset=results[2]; - adm1021_write_value(client, - ADM1021_REG_REM_OFFSET, - data->remote_temp_offset); + prec = ((results[2]-((results[2]/1000)*1000))/125)<<5; + adm1021_write_value(client, ADM1021_REG_REM_OFFSET_PREC, prec); + results[2]=results[2]/1000; + data->remote_temp_offset_prec=prec; + data->remote_temp_offset=results[2]; + adm1021_write_value(client, ADM1021_REG_REM_OFFSET, data->remote_temp_offset); } } } } +__attribute__((unused)) static void adm1021_die_code(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { @@ -549,6 +522,7 @@ } } +__attribute__((unused)) static void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { @@ -574,8 +548,8 @@ i2c_del_driver(&adm1021_driver); } -MODULE_AUTHOR - ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>"); +MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and " + "Philip Edelbrock <phil@netroedge.com>"); MODULE_DESCRIPTION("adm1021 driver"); MODULE_LICENSE("GPL"); ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.7, 2003/04/02 11:34:13-08:00, greg@kroah.com i2c: remove sysctl and proc functions from via686a.c driver This still needs to be converted to use sysfs files, but due to lack of hardware, I can not do this. This change is necessary as the sysctl and proc interface is about to go away. drivers/i2c/chips/via686a.c | 56 +++++++++++++------------------------------- 1 files changed, 17 insertions(+), 39 deletions(-) diff -Nru a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c --- a/drivers/i2c/chips/via686a.c Wed Apr 2 16:01:14 2003 +++ b/drivers/i2c/chips/via686a.c Wed Apr 2 16:01:14 2003 @@ -369,8 +369,6 @@ dynamically allocated, at the same time when a new via686a client is allocated. */ struct via686a_data { - int sysctl_id; - struct semaphore update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -390,8 +388,7 @@ static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ static int via686a_attach_adapter(struct i2c_adapter *adapter); -static int via686a_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind); +static int via686a_detect(struct i2c_adapter *adapter, int address, int kind); static int via686a_detach_client(struct i2c_client *client); static int via686a_read_value(struct i2c_client *client, u8 register); @@ -400,18 +397,6 @@ static void via686a_update_client(struct i2c_client *client); static void via686a_init_client(struct i2c_client *client); - -static void via686a_in(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void via686a_fan(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void via686a_temp(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void via686a_alarms(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); -static void via686a_fan_div(struct i2c_client *client, int operation, - int ctl_name, int *nrels_mag, long *results); - static int via686a_id = 0; /* The driver. I choose to use type i2c_driver, as at is identical to both @@ -457,6 +442,7 @@ /* -- SENSORS SYSCTL END -- */ +#if 0 /* These files are created for each detected VIA686A. This is just a template; though at first sight, you might think we could use a statically allocated list, we need some way to get back to the parent - which @@ -489,6 +475,7 @@ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms}, {0} }; +#endif static inline int via686a_read_value(struct i2c_client *client, u8 reg) { @@ -507,15 +494,12 @@ return i2c_detect(adapter, &addr_data, via686a_detect); } -int via686a_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind) +static int via686a_detect(struct i2c_adapter *adapter, int address, int kind) { - int i; struct i2c_client *new_client; struct via686a_data *data; int err = 0; - const char *type_name = "via686a"; - const char client_name[] = "via686a chip"; + const char *name = "via686a"; u16 val; /* Make sure we are probing the ISA bus!! */ @@ -570,7 +554,7 @@ new_client->flags = 0; /* Fill in the remaining client fields and put into the global list */ - snprintf(new_client->dev.name, DEVICE_NAME_SIZE, client_name); + snprintf(new_client->dev.name, DEVICE_NAME_SIZE, name); new_client->id = via686a_id++; data->valid = 0; @@ -579,21 +563,10 @@ if ((err = i2c_attach_client(new_client))) goto ERROR3; - /* Register a new directory entry with module sensors */ - if ((i = i2c_register_entry((struct i2c_client *) new_client, - type_name, - via686a_dir_table_template)) < 0) { - err = i; - goto ERROR4; - } - data->sysctl_id = i; - /* Initialize the VIA686A chip */ via686a_init_client(new_client); return 0; - ERROR4: - i2c_detach_client(new_client); ERROR3: release_region(address, VIA686A_EXTENT); kfree(new_client); @@ -604,8 +577,6 @@ static int via686a_detach_client(struct i2c_client *client) { int err; - struct via686a_data *data = i2c_get_clientdata(client); - i2c_deregister_entry(data->sysctl_id); if ((err = i2c_detach_client(client))) { dev_err(&client->dev, @@ -752,6 +723,9 @@ large enough (by checking the incoming value of *nrels). This is not very good practice, but as long as you put less than about 5 values in results, you can assume it is large enough. */ +/* FIXME, remove these functions, they are here to verify the sysfs conversion + * is correct, or not */ +__attribute__((unused)) static void via686a_in(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { @@ -780,7 +754,8 @@ } } -void via686a_fan(struct i2c_client *client, int operation, int ctl_name, +__attribute__((unused)) +static void via686a_fan(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { struct via686a_data *data = i2c_get_clientdata(client); @@ -808,7 +783,8 @@ } } -void via686a_temp(struct i2c_client *client, int operation, int ctl_name, +__attribute__((unused)) +static void via686a_temp(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { struct via686a_data *data = i2c_get_clientdata(client); @@ -838,7 +814,8 @@ } } -void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, +__attribute__((unused)) +static void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { struct via686a_data *data = i2c_get_clientdata(client); @@ -851,7 +828,8 @@ } } -void via686a_fan_div(struct i2c_client *client, int operation, +__attribute__((unused)) +static void via686a_fan_div(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { struct via686a_data *data = i2c_get_clientdata(client); ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 6:21 ` Albert Cranford 0 siblings, 2 replies; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.8, 2003/04/02 11:45:21-08:00, greg@kroah.com i2c: remove proc and sysctl code from i2c-proc as it is no longer used. drivers/i2c/i2c-proc.c | 546 ----------------------------------------------- include/linux/i2c-proc.h | 40 --- 2 files changed, 586 deletions(-) diff -Nru a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c --- a/drivers/i2c/i2c-proc.c Wed Apr 2 16:01:04 2003 +++ b/drivers/i2c/i2c-proc.c Wed Apr 2 16:01:04 2003 @@ -37,539 +37,6 @@ #include <linux/i2c-proc.h> #include <asm/uaccess.h> -static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, - long *results, int magnitude); -static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, - long *results, int magnitude); -static int i2c_proc_chips(ctl_table * ctl, int write, - struct file *filp, void *buffer, - size_t * lenp); -static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, - void *oldval, size_t * oldlenp, - void *newval, size_t newlen, - void **context); - -#define SENSORS_ENTRY_MAX 20 -static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; - -static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; - -static ctl_table i2c_proc_dev_sensors[] = { - {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, - &i2c_sysctl_chips}, - {0} -}; - -static ctl_table i2c_proc_dev[] = { - {DEV_SENSORS, "sensors", NULL, 0, 0555, i2c_proc_dev_sensors}, - {0}, -}; - - -static ctl_table i2c_proc[] = { - {CTL_DEV, "dev", NULL, 0, 0555, i2c_proc_dev}, - {0} -}; - - -static struct ctl_table_header *i2c_proc_header; - -/* This returns a nice name for a new directory; for example lm78-isa-0310 - (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for - a LM75 chip on the third i2c bus at address 0x4e). - name is allocated first. */ -static char *generate_name(struct i2c_client *client, const char *prefix) -{ - struct i2c_adapter *adapter = client->adapter; - int addr = client->addr; - char name_buffer[50], *name; - - if (i2c_is_isa_adapter(adapter)) { - sprintf(name_buffer, "%s-isa-%04x", prefix, addr); - } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) { - int id = i2c_adapter_id(adapter); - if (id < 0) - return ERR_PTR(-ENOENT); - sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); - } else { /* dummy adapter, generate prefix */ - int end, i; - - sprintf(name_buffer, "%s-", prefix); - end = strlen(name_buffer); - - for (i = 0; i < 32; i++) { - if (adapter->algo->name[i] == ' ') - break; - name_buffer[end++] = tolower(adapter->algo->name[i]); - } - - name_buffer[end] = 0; - sprintf(name_buffer + end, "-%04x", addr); - } - - name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); - if (unlikely(!name)) - return ERR_PTR(-ENOMEM); - strcpy(name, name_buffer); - return name; -} - -/* This rather complex function must be called when you want to add an entry - to /proc/sys/dev/sensors/chips. It also creates a new directory within - /proc/sys/dev/sensors/. - ctl_template should be a template of the newly created directory. It is - copied in memory. The extra2 field of each file is set to point to client. - If any driver wants subdirectories within the newly created directory, - this function must be updated! */ -int i2c_register_entry(struct i2c_client *client, const char *prefix, - struct ctl_table *leaf) -{ - struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl; - struct ctl_table_header *hdr; - struct ctl_table *tmp; - const char *name; - int id; - - name = generate_name(client, prefix); - if (IS_ERR(name)) - return PTR_ERR(name); - - for (id = 0; id < SENSORS_ENTRY_MAX; id++) { - if (!i2c_entries[id]) - goto free_slot; - } - - goto out_free_name; - - free_slot: - tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); - if (unlikely(!tbl)) - goto out_free_name; - memset(tbl, 0, sizeof(*tbl)); - - for (tmp = leaf; tmp->ctl_name; tmp++) - tmp->extra2 = client; - - tbl->sensors->ctl_name = id+256; - tbl->sensors->procname = name; - tbl->sensors->mode = 0555; - tbl->sensors->child = leaf; - - tbl->dev->ctl_name = DEV_SENSORS; - tbl->dev->procname = "sensors"; - tbl->dev->mode = 0555; - tbl->dev->child = tbl->sensors; - - tbl->root->ctl_name = CTL_DEV; - tbl->root->procname = "dev"; - tbl->root->mode = 0555; - tbl->root->child = tbl->dev; - - hdr = register_sysctl_table(tbl->root, 0); - if (unlikely(!hdr)) - goto out_free_tbl; - - i2c_entries[id] = hdr; - i2c_clients[id] = client; - - return (id + 256); /* XXX(hch) why?? */ - - out_free_tbl: - kfree(tbl); - out_free_name: - kfree(name); - return -ENOMEM; -} - -void i2c_deregister_entry(int id) -{ - id -= 256; - - if (i2c_entries[id]) { - struct ctl_table_header *hdr = i2c_entries[id]; - struct ctl_table *tbl = hdr->ctl_table; - - unregister_sysctl_table(hdr); - kfree(tbl->child->child->procname); - kfree(tbl); /* actually the whole anonymous struct */ - } - - i2c_entries[id] = NULL; - i2c_clients[id] = NULL; -} - -static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, - void *buffer, size_t * lenp) -{ - char BUF[SENSORS_PREFIX_MAX + 30]; - int buflen, curbufsize, i; - struct ctl_table *client_tbl; - - if (write) - return 0; - - /* If buffer is size 0, or we try to read when not at the start, we - return nothing. Note that I think writing when not at the start - does not work either, but anyway, this is straight from the kernel - sources. */ - if (!*lenp || (filp->f_pos && !write)) { - *lenp = 0; - return 0; - } - curbufsize = 0; - for (i = 0; i < SENSORS_ENTRY_MAX; i++) - if (i2c_entries[i]) { - client_tbl = - i2c_entries[i]->ctl_table->child->child; - buflen = - sprintf(BUF, "%d\t%s\n", client_tbl->ctl_name, - client_tbl->procname); - if (buflen + curbufsize > *lenp) - buflen = *lenp - curbufsize; - if(copy_to_user(buffer, BUF, buflen)) - return -EFAULT; - curbufsize += buflen; - (char *) buffer += buflen; - } - *lenp = curbufsize; - filp->f_pos += curbufsize; - return 0; -} - -static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, - void *oldval, size_t * oldlenp, void *newval, - size_t newlen, void **context) -{ - struct i2c_chips_data data; - int i, oldlen, nrels, maxels,ret=0; - struct ctl_table *client_tbl; - - if (oldval && oldlenp && !((ret = get_user(oldlen, oldlenp))) && - oldlen) { - maxels = oldlen / sizeof(struct i2c_chips_data); - nrels = 0; - for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels); - i++) - if (i2c_entries[i]) { - client_tbl = - i2c_entries[i]->ctl_table->child-> - child; - data.sysctl_id = client_tbl->ctl_name; - strcpy(data.name, client_tbl->procname); - if(copy_to_user(oldval, &data, - sizeof(struct - i2c_chips_data))) - return -EFAULT; - (char *) oldval += - sizeof(struct i2c_chips_data); - nrels++; - } - oldlen = nrels * sizeof(struct i2c_chips_data); - if(put_user(oldlen, oldlenp)) - return -EFAULT; - } - return ret; -} - - -/* This function reads or writes a 'real' value (encoded by the combination - of an integer and a magnitude, the last is the power of ten the value - should be divided with) to a /proc/sys directory. To use this function, - you must (before registering the ctl_table) set the extra2 field to the - client, and the extra1 field to a function of the form: - void func(struct i2c_client *client, int operation, int ctl_name, - int *nrels_mag, long *results) - This function can be called for three values of operation. If operation - equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in - nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should - be read into results. nrels_mag should return the number of elements - read; the maximum number is put in it on entry. Finally, if operation - equals SENSORS_PROC_REAL_WRITE, the values in results should be - written to the chip. nrels_mag contains on entry the number of elements - found. - In all cases, client points to the client we wish to interact with, - and ctl_name is the SYSCTL id of the file we are accessing. */ -int i2c_proc_real(ctl_table * ctl, int write, struct file *filp, - void *buffer, size_t * lenp) -{ -#define MAX_RESULTS 32 - int mag, nrels = MAX_RESULTS; - long results[MAX_RESULTS]; - i2c_real_callback callback = ctl->extra1; - struct i2c_client *client = ctl->extra2; - int res; - - /* If buffer is size 0, or we try to read when not at the start, we - return nothing. Note that I think writing when not at the start - does not work either, but anyway, this is straight from the kernel - sources. */ - if (!*lenp || (filp->f_pos && !write)) { - *lenp = 0; - return 0; - } - - /* Get the magnitude */ - callback(client, SENSORS_PROC_REAL_INFO, ctl->ctl_name, &mag, - NULL); - - if (write) { - /* Read the complete input into results, converting to longs */ - res = i2c_parse_reals(&nrels, buffer, *lenp, results, mag); - if (res) - return res; - - if (!nrels) - return 0; - - /* Now feed this information back to the client */ - callback(client, SENSORS_PROC_REAL_WRITE, ctl->ctl_name, - &nrels, results); - - filp->f_pos += *lenp; - return 0; - } else { /* read */ - /* Get the information from the client into results */ - callback(client, SENSORS_PROC_REAL_READ, ctl->ctl_name, - &nrels, results); - - /* And write them to buffer, converting to reals */ - res = i2c_write_reals(nrels, buffer, lenp, results, mag); - if (res) - return res; - filp->f_pos += *lenp; - return 0; - } -} - -/* This function is equivalent to i2c_proc_real, only it interacts with - the sysctl(2) syscall, and returns no reals, but integers */ -int i2c_sysctl_real(ctl_table * table, int *name, int nlen, - void *oldval, size_t * oldlenp, void *newval, - size_t newlen, void **context) -{ - long results[MAX_RESULTS]; - int oldlen, nrels = MAX_RESULTS,ret=0; - i2c_real_callback callback = table->extra1; - struct i2c_client *client = table->extra2; - - /* Check if we need to output the old values */ - if (oldval && oldlenp && !((ret=get_user(oldlen, oldlenp))) && oldlen) { - callback(client, SENSORS_PROC_REAL_READ, table->ctl_name, - &nrels, results); - - /* Note the rounding factor! */ - if (nrels * sizeof(long) < oldlen) - oldlen = nrels * sizeof(long); - oldlen = (oldlen / sizeof(long)) * sizeof(long); - if(copy_to_user(oldval, results, oldlen)) - return -EFAULT; - if(put_user(oldlen, oldlenp)) - return -EFAULT; - } - - if (newval && newlen) { - /* Note the rounding factor! */ - newlen -= newlen % sizeof(long); - nrels = newlen / sizeof(long); - if(copy_from_user(results, newval, newlen)) - return -EFAULT; - - /* Get the new values back to the client */ - callback(client, SENSORS_PROC_REAL_WRITE, table->ctl_name, - &nrels, results); - } - return ret; -} - - -/* nrels contains initially the maximum number of elements which can be - put in results, and finally the number of elements actually put there. - A magnitude of 1 will multiply everything with 10; etc. - buffer, bufsize is the character buffer we read from and its length. - results will finally contain the parsed integers. - - Buffer should contain several reals, separated by whitespace. A real - has the following syntax: - [ Minus ] Digit* [ Dot Digit* ] - (everything between [] is optional; * means zero or more). - When the next character is unparsable, everything is skipped until the - next whitespace. - - WARNING! This is tricky code. I have tested it, but there may still be - hidden bugs in it, even leading to crashes and things! -*/ -static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, - long *results, int magnitude) -{ - int maxels, min, mag; - long res,ret=0; - char nextchar = 0; - - maxels = *nrels; - *nrels = 0; - - while (bufsize && (*nrels < maxels)) { - - /* Skip spaces at the start */ - while (bufsize && - !((ret=get_user(nextchar, (char *) buffer))) && - isspace((int) nextchar)) { - bufsize--; - ((char *) buffer)++; - } - - if (ret) - return -EFAULT; - /* Well, we may be done now */ - if (!bufsize) - return 0; - - /* New defaults for our result */ - min = 0; - res = 0; - mag = magnitude; - - /* Check for a minus */ - if (!((ret=get_user(nextchar, (char *) buffer))) - && (nextchar == '-')) { - min = 1; - bufsize--; - ((char *) buffer)++; - } - if (ret) - return -EFAULT; - - /* Digits before a decimal dot */ - while (bufsize && - !((ret=get_user(nextchar, (char *) buffer))) && - isdigit((int) nextchar)) { - res = res * 10 + nextchar - '0'; - bufsize--; - ((char *) buffer)++; - } - if (ret) - return -EFAULT; - - /* If mag < 0, we must actually divide here! */ - while (mag < 0) { - res = res / 10; - mag++; - } - - if (bufsize && (nextchar == '.')) { - /* Skip the dot */ - bufsize--; - ((char *) buffer)++; - - /* Read digits while they are significant */ - while (bufsize && (mag > 0) && - !((ret=get_user(nextchar, (char *) buffer))) && - isdigit((int) nextchar)) { - res = res * 10 + nextchar - '0'; - mag--; - bufsize--; - ((char *) buffer)++; - } - if (ret) - return -EFAULT; - } - /* If we are out of data, but mag > 0, we need to scale here */ - while (mag > 0) { - res = res * 10; - mag--; - } - - /* Skip everything until we hit whitespace */ - while (bufsize && - !((ret=get_user(nextchar, (char *) buffer))) && - isspace((int) nextchar)) { - bufsize--; - ((char *) buffer)++; - } - if (ret) - return -EFAULT; - - /* Put res in results */ - results[*nrels] = (min ? -1 : 1) * res; - (*nrels)++; - } - - /* Well, there may be more in the buffer, but we need no more data. - Ignore anything that is left. */ - return 0; -} - -static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, - long *results, int magnitude) -{ -#define BUFLEN 20 - char BUF[BUFLEN + 1]; /* An individual representation should fit! */ - char printfstr[10]; - int nr = 0; - int buflen, mag, times; - int curbufsize = 0; - - while ((nr < nrels) && (curbufsize < *bufsize)) { - mag = magnitude; - - if (nr != 0) { - if(put_user(' ', (char *) buffer)) - return -EFAULT; - curbufsize++; - ((char *) buffer)++; - } - - /* Fill BUF with the representation of the next string */ - if (mag <= 0) { - buflen = sprintf(BUF, "%ld", results[nr]); - if (buflen < 0) { /* Oops, a sprintf error! */ - *bufsize = 0; - return -EINVAL; - } - while ((mag < 0) && (buflen < BUFLEN)) { - BUF[buflen++] = '0'; - mag++; - } - BUF[buflen] = 0; - } else { - times = 1; - for (times = 1; mag-- > 0; times *= 10); - if (results[nr] < 0) { - BUF[0] = '-'; - buflen = 1; - } else - buflen = 0; - strcpy(printfstr, "%ld.%0Xld"); - printfstr[6] = magnitude + '0'; - buflen += - sprintf(BUF + buflen, printfstr, - abs(results[nr]) / times, - abs(results[nr]) % times); - if (buflen < 0) { /* Oops, a sprintf error! */ - *bufsize = 0; - return -EINVAL; - } - } - - /* Now copy it to the user-space buffer */ - if (buflen + curbufsize > *bufsize) - buflen = *bufsize - curbufsize; - if(copy_to_user(buffer, BUF, buflen)) - return -EFAULT; - curbufsize += buflen; - (char *) buffer += buflen; - - nr++; - } - if (curbufsize < *bufsize) { - if(put_user('\n', (char *) buffer)) - return -EFAULT; - curbufsize++; - } - *bufsize = curbufsize; - return 0; -} - /* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ int i2c_detect(struct i2c_adapter *adapter, @@ -703,26 +170,13 @@ static int __init i2c_proc_init(void) { - printk(KERN_INFO "i2c-proc.o version %s (%s)\n", I2C_VERSION, I2C_DATE); - if (! - (i2c_proc_header = - register_sysctl_table(i2c_proc, 0))) { - printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); - return -EPERM; - } - i2c_proc_header->ctl_table->child->de->owner = THIS_MODULE; return 0; } static void __exit i2c_proc_exit(void) { - unregister_sysctl_table(i2c_proc_header); } -EXPORT_SYMBOL(i2c_register_entry); -EXPORT_SYMBOL(i2c_deregister_entry); -EXPORT_SYMBOL(i2c_proc_real); -EXPORT_SYMBOL(i2c_sysctl_real); EXPORT_SYMBOL(i2c_detect); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); diff -Nru a/include/linux/i2c-proc.h b/include/linux/i2c-proc.h --- a/include/linux/i2c-proc.h Wed Apr 2 16:01:04 2003 +++ b/include/linux/i2c-proc.h Wed Apr 2 16:01:04 2003 @@ -34,46 +34,6 @@ #define SENSORS_PROC_REAL_READ 2 #define SENSORS_PROC_REAL_WRITE 3 -/* These funcion reads or writes a 'real' value (encoded by the combination - of an integer and a magnitude, the last is the power of ten the value - should be divided with) to a /proc/sys directory. To use these functions, - you must (before registering the ctl_table) set the extra2 field to the - client, and the extra1 field to a function of the form: - void func(struct i2c_client *client, int operation, int ctl_name, - int *nrels_mag, long *results) - This last function can be called for three values of operation. If - operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned - in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should - be read into results. nrels_mag should return the number of elements - read; the maximum number is put in it on entry. Finally, if operation - equals SENSORS_PROC_REAL_WRITE, the values in results should be - written to the chip. nrels_mag contains on entry the number of elements - found. - In all cases, client points to the client we wish to interact with, - and ctl_name is the SYSCTL id of the file we are accessing. */ -extern int i2c_sysctl_real(ctl_table * table, int *name, int nlen, - void *oldval, size_t * oldlenp, - void *newval, size_t newlen, - void **context); -extern int i2c_proc_real(ctl_table * ctl, int write, struct file *filp, - void *buffer, size_t * lenp); - - - -/* These rather complex functions must be called when you want to add or - delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It - also creates a new directory within /proc/sys/dev/sensors/. - ctl_template should be a template of the newly created directory. It is - copied in memory. The extra2 field of each file is set to point to client. - If any driver wants subdirectories within the newly created directory, - these functions must be updated! */ -extern int i2c_register_entry(struct i2c_client *client, - const char *prefix, - ctl_table * ctl_template); - -extern void i2c_deregister_entry(int id); - - /* A structure containing detect information. Force variables overrule all other variables; they force a detection on that place. If a specific chip is given, the module blindly assumes this ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 6:21 ` Albert Cranford 1 sibling, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.9, 2003/04/02 11:48:13-08:00, greg@kroah.com i2c: remove unused paramater in found_proc callback function. (the users of this function have already been changed in previous patches) drivers/i2c/i2c-proc.c | 4 ++-- include/linux/i2c-proc.h | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff -Nru a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c --- a/drivers/i2c/i2c-proc.c Wed Apr 2 16:00:55 2003 +++ b/drivers/i2c/i2c-proc.c Wed Apr 2 16:00:55 2003 @@ -69,7 +69,7 @@ ((this_force->force[j] == SENSORS_ANY_I2C_BUS) && !is_isa)) && (addr == this_force->force[j + 1]) ) { dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr); - if ((err = found_proc(adapter, addr, 0, this_force->kind))) + if ((err = found_proc(adapter, addr, this_force->kind))) return err; found = 1; } @@ -162,7 +162,7 @@ whether there is some client here at all! */ if (is_isa || (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0)) - if ((err = found_proc(adapter, addr, 0, -1))) + if ((err = found_proc(adapter, addr, -1))) return err; } return 0; diff -Nru a/include/linux/i2c-proc.h b/include/linux/i2c-proc.h --- a/include/linux/i2c-proc.h Wed Apr 2 16:00:55 2003 +++ b/include/linux/i2c-proc.h Wed Apr 2 16:00:55 2003 @@ -328,8 +328,7 @@ SENSORS_INSMOD typedef int i2c_found_addr_proc(struct i2c_adapter *adapter, - int addr, unsigned short flags, - int kind); + int addr, int kind); /* Detect function. It iterates over all possible addresses itself. For SMBus addresses, it will only call found_proc if some client is connected ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.10, 2003/04/02 12:01:21-08:00, greg@kroah.com i2c: move i2c-proc to i2c-sensor and clean up all usages of it. drivers/i2c/i2c-proc.c | 187 ---------------------- include/linux/i2c-proc.h | 373 -------------------------------------------- drivers/i2c/Kconfig | 11 - drivers/i2c/Makefile | 2 drivers/i2c/chips/Kconfig | 13 + drivers/i2c/chips/adm1021.c | 2 drivers/i2c/chips/lm75.c | 2 drivers/i2c/chips/via686a.c | 2 drivers/i2c/i2c-sensor.c | 182 +++++++++++++++++++++ include/linux/i2c-sensor.h | 373 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 568 insertions(+), 579 deletions(-) diff -Nru a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig --- a/drivers/i2c/Kconfig Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/Kconfig Wed Apr 2 16:00:46 2003 @@ -196,17 +196,6 @@ <file:Documentation/modules.txt>. The module will be called i2c-dev. -config I2C_PROC - tristate "I2C /proc interface (required for hardware sensors)" - depends on I2C && SYSCTL - help - This provides support for i2c device entries in the /proc filesystem. - The entries will be found in /proc/sys/dev/sensors. - - This code is also available as a module. If you want to compile - it as a module, say M here and read <file:Documentation/modules.txt>. - The module will be called i2c-proc. - source drivers/i2c/busses/Kconfig source drivers/i2c/chips/Kconfig diff -Nru a/drivers/i2c/Makefile b/drivers/i2c/Makefile --- a/drivers/i2c/Makefile Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/Makefile Wed Apr 2 16:00:46 2003 @@ -14,5 +14,5 @@ obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o -obj-$(CONFIG_I2C_PROC) += i2c-proc.o +obj-$(CONFIG_I2C_SENSOR) += i2c-sensor.o obj-y += busses/ chips/ diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:46 2003 @@ -1,13 +1,13 @@ # # Sensor device configuration -# All depend on EXPERIMENTAL, I2C and I2C_PROC. +# All depend on EXPERIMENTAL and I2C # menu "I2C Hardware Sensors Chip support" config SENSORS_ADM1021 tristate " Analog Devices ADM1021 and compatibles" - depends on I2C && I2C_PROC + depends on I2C && EXPERIMENTAL help If you say yes here you get support for Analog Devices ADM1021 and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, @@ -24,7 +24,7 @@ config SENSORS_LM75 tristate " National Semiconductors LM75 and compatibles" - depends on I2C && I2C_PROC + depends on I2C && EXPERIMENTAL help If you say yes here you get support for National Semiconductor LM75 sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon @@ -39,7 +39,7 @@ config SENSORS_VIA686A tristate " VIA686A" - depends on I2C && I2C_PROC + depends on I2C && EXPERIMENTAL help support for via686a If you say yes here you get support for the integrated sensors in @@ -49,5 +49,10 @@ You will also need the latest user-space utilties: you can find them in the lm_sensors package, which you can download at http://www.lm-sensors.nu + +config I2C_SENSOR + tristate + depends on SENSORS_ADM1021 || SENSORS_LM75 || SENSORS_VIA686A + default m endmenu diff -Nru a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c --- a/drivers/i2c/chips/adm1021.c Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/chips/adm1021.c Wed Apr 2 16:00:46 2003 @@ -23,7 +23,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/i2c-proc.h> +#include <linux/i2c-sensor.h> /* Registers */ diff -Nru a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c --- a/drivers/i2c/chips/lm75.c Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/chips/lm75.c Wed Apr 2 16:00:46 2003 @@ -24,7 +24,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/i2c-proc.h> +#include <linux/i2c-sensor.h> /* Addresses to scan */ diff -Nru a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c --- a/drivers/i2c/chips/via686a.c Wed Apr 2 16:00:46 2003 +++ b/drivers/i2c/chips/via686a.c Wed Apr 2 16:00:46 2003 @@ -36,7 +36,7 @@ #include <linux/pci.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/i2c-proc.h> +#include <linux/i2c-sensor.h> #include <linux/init.h> #include <asm/io.h> diff -Nru a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c --- a/drivers/i2c/i2c-proc.c Wed Apr 2 16:00:46 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,187 +0,0 @@ -/* - i2c-proc.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl> and - Mark D. Studebaker <mdsxyz123@yahoo.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - This driver puts entries in /proc/sys/dev/sensors for each I2C device -*/ - -/* #define DEBUG 1 */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/ctype.h> -#include <linux/sysctl.h> -#include <linux/proc_fs.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/i2c.h> -#include <linux/i2c-proc.h> -#include <asm/uaccess.h> - - -/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ -int i2c_detect(struct i2c_adapter *adapter, - struct i2c_address_data *address_data, - i2c_found_addr_proc * found_proc) -{ - int addr, i, found, j, err; - struct i2c_force_data *this_force; - int is_isa = i2c_is_isa_adapter(adapter); - int adapter_id = - is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter); - - /* Forget it if we can't probe using SMBUS_QUICK */ - if ((!is_isa) && - !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) - return -1; - - for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { - /* XXX: WTF is going on here??? */ - if ((is_isa && check_region(addr, 1)) || - (!is_isa && i2c_check_addr(adapter, addr))) - continue; - - /* If it is in one of the force entries, we don't do any - detection at all */ - found = 0; - for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) { - for (j = 0; !found && (this_force->force[j] != SENSORS_I2C_END); j += 2) { - if ( ((adapter_id == this_force->force[j]) || - ((this_force->force[j] == SENSORS_ANY_I2C_BUS) && !is_isa)) && - (addr == this_force->force[j + 1]) ) { - dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr); - if ((err = found_proc(adapter, addr, this_force->kind))) - return err; - found = 1; - } - } - } - if (found) - continue; - - /* If this address is in one of the ignores, we can forget about it - right now */ - for (i = 0; !found && (address_data->ignore[i] != SENSORS_I2C_END); i += 2) { - if ( ((adapter_id == address_data->ignore[i]) || - ((address_data->ignore[i] == SENSORS_ANY_I2C_BUS) && - !is_isa)) && - (addr == address_data->ignore[i + 1])) { - dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr); - found = 1; - } - } - for (i = 0; !found && (address_data->ignore_range[i] != SENSORS_I2C_END); i += 3) { - if ( ((adapter_id == address_data->ignore_range[i]) || - ((address_data-> ignore_range[i] == SENSORS_ANY_I2C_BUS) & - !is_isa)) && - (addr >= address_data->ignore_range[i + 1]) && - (addr <= address_data->ignore_range[i + 2])) { - dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr); - found = 1; - } - } - if (found) - continue; - - /* Now, we will do a detection, but only if it is in the normal or - probe entries */ - if (is_isa) { - for (i = 0; !found && (address_data->normal_isa[i] != SENSORS_ISA_END); i += 1) { - if (addr == address_data->normal_isa[i]) { - dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr); - found = 1; - } - } - for (i = 0; !found && (address_data->normal_isa_range[i] != SENSORS_ISA_END); i += 3) { - if ((addr >= address_data->normal_isa_range[i]) && - (addr <= address_data->normal_isa_range[i + 1]) && - ((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) { - dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr); - found = 1; - } - } - } else { - for (i = 0; !found && (address_data->normal_i2c[i] != SENSORS_I2C_END); i += 1) { - if (addr == address_data->normal_i2c[i]) { - found = 1; - dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr); - } - } - for (i = 0; !found && (address_data->normal_i2c_range[i] != SENSORS_I2C_END); i += 2) { - if ((addr >= address_data->normal_i2c_range[i]) && - (addr <= address_data->normal_i2c_range[i + 1])) { - dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr); - found = 1; - } - } - } - - for (i = 0; - !found && (address_data->probe[i] != SENSORS_I2C_END); - i += 2) { - if (((adapter_id == address_data->probe[i]) || - ((address_data-> - probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) - && (addr == address_data->probe[i + 1])) { - dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr); - found = 1; - } - } - for (i = 0; !found && (address_data->probe_range[i] != SENSORS_I2C_END); i += 3) { - if ( ((adapter_id == address_data->probe_range[i]) || - ((address_data->probe_range[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) && - (addr >= address_data->probe_range[i + 1]) && - (addr <= address_data->probe_range[i + 2])) { - found = 1; - dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr); - } - } - if (!found) - continue; - - /* OK, so we really should examine this address. First check - whether there is some client here at all! */ - if (is_isa || - (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0)) - if ((err = found_proc(adapter, addr, -1))) - return err; - } - return 0; -} - -static int __init i2c_proc_init(void) -{ - return 0; -} - -static void __exit i2c_proc_exit(void) -{ -} - -EXPORT_SYMBOL(i2c_detect); - -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); -MODULE_DESCRIPTION("i2c-proc driver"); -MODULE_LICENSE("GPL"); - -module_init(i2c_proc_init); -module_exit(i2c_proc_exit); diff -Nru a/drivers/i2c/i2c-sensor.c b/drivers/i2c/i2c-sensor.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-sensor.c Wed Apr 2 16:00:46 2003 @@ -0,0 +1,182 @@ +/* + i2c-sensor.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl> and + Mark D. Studebaker <mdsxyz123@yahoo.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* #define DEBUG 1 */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/ctype.h> +#include <linux/sysctl.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/i2c-sensor.h> +#include <asm/uaccess.h> + + +/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ +int i2c_detect(struct i2c_adapter *adapter, + struct i2c_address_data *address_data, + i2c_found_addr_proc * found_proc) +{ + int addr, i, found, j, err; + struct i2c_force_data *this_force; + int is_isa = i2c_is_isa_adapter(adapter); + int adapter_id = + is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if ((!is_isa) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { + /* XXX: WTF is going on here??? */ + if ((is_isa && check_region(addr, 1)) || + (!is_isa && i2c_check_addr(adapter, addr))) + continue; + + /* If it is in one of the force entries, we don't do any + detection at all */ + found = 0; + for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) { + for (j = 0; !found && (this_force->force[j] != SENSORS_I2C_END); j += 2) { + if ( ((adapter_id == this_force->force[j]) || + ((this_force->force[j] == SENSORS_ANY_I2C_BUS) && !is_isa)) && + (addr == this_force->force[j + 1]) ) { + dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr); + if ((err = found_proc(adapter, addr, this_force->kind))) + return err; + found = 1; + } + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; !found && (address_data->ignore[i] != SENSORS_I2C_END); i += 2) { + if ( ((adapter_id == address_data->ignore[i]) || + ((address_data->ignore[i] == SENSORS_ANY_I2C_BUS) && + !is_isa)) && + (addr == address_data->ignore[i + 1])) { + dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->ignore_range[i] != SENSORS_I2C_END); i += 3) { + if ( ((adapter_id == address_data->ignore_range[i]) || + ((address_data-> ignore_range[i] == SENSORS_ANY_I2C_BUS) & + !is_isa)) && + (addr >= address_data->ignore_range[i + 1]) && + (addr <= address_data->ignore_range[i + 2])) { + dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + if (is_isa) { + for (i = 0; !found && (address_data->normal_isa[i] != SENSORS_ISA_END); i += 1) { + if (addr == address_data->normal_isa[i]) { + dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->normal_isa_range[i] != SENSORS_ISA_END); i += 3) { + if ((addr >= address_data->normal_isa_range[i]) && + (addr <= address_data->normal_isa_range[i + 1]) && + ((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) { + dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr); + found = 1; + } + } + } else { + for (i = 0; !found && (address_data->normal_i2c[i] != SENSORS_I2C_END); i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr); + } + } + for (i = 0; !found && (address_data->normal_i2c_range[i] != SENSORS_I2C_END); i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i + 1])) { + dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + } + + for (i = 0; + !found && (address_data->probe[i] != SENSORS_I2C_END); + i += 2) { + if (((adapter_id == address_data->probe[i]) || + ((address_data-> + probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) + && (addr == address_data->probe[i + 1])) { + dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->probe_range[i] != SENSORS_I2C_END); i += 3) { + if ( ((adapter_id == address_data->probe_range[i]) || + ((address_data->probe_range[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) && + (addr >= address_data->probe_range[i + 1]) && + (addr <= address_data->probe_range[i + 2])) { + found = 1; + dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (is_isa || + (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0)) + if ((err = found_proc(adapter, addr, -1))) + return err; + } + return 0; +} + +static int __init i2c_sensor_init(void) +{ + return 0; +} + +static void __exit i2c_sensor_exit(void) +{ +} + +EXPORT_SYMBOL(i2c_detect); + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); +MODULE_DESCRIPTION("i2c-sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_sensor_init); +module_exit(i2c_sensor_exit); diff -Nru a/include/linux/i2c-proc.h b/include/linux/i2c-proc.h --- a/include/linux/i2c-proc.h Wed Apr 2 16:00:46 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,373 +0,0 @@ -/* - i2c-proc.h - Part of the i2c package - was originally sensors.h - Part of lm_sensors, Linux kernel modules - for hardware monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _LINUX_I2C_PROC_H -#define _LINUX_I2C_PROC_H - -#include <linux/sysctl.h> - -/* The type of callback functions used in sensors_{proc,sysctl}_real */ -typedef void (*i2c_real_callback) (struct i2c_client * client, - int operation, int ctl_name, - int *nrels_mag, long *results); - -/* Values for the operation field in the above function type */ -#define SENSORS_PROC_REAL_INFO 1 -#define SENSORS_PROC_REAL_READ 2 -#define SENSORS_PROC_REAL_WRITE 3 - -/* A structure containing detect information. - Force variables overrule all other variables; they force a detection on - that place. If a specific chip is given, the module blindly assumes this - chip type is present; if a general force (kind == 0) is given, the module - will still try to figure out what type of chip is present. This is useful - if for some reasons the detect for SMBus or ISA address space filled - fails. - probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. - A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for - the ISA bus, -1 for any I2C bus), the second is the address. - kind: The kind of chip. 0 equals any chip. -*/ -struct i2c_force_data { - unsigned short *force; - unsigned short kind; -}; - -/* A structure containing the detect information. - normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END. - A list of I2C addresses which should normally be examined. - normal_i2c_range: filled in by the module writer. Terminated by - SENSORS_I2C_END - A list of pairs of I2C addresses, each pair being an inclusive range of - addresses which should normally be examined. - normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. - A list of ISA addresses which should normally be examined. - normal_isa_range: filled in by the module writer. Terminated by - SENSORS_ISA_END - A list of triples. The first two elements are ISA addresses, being an - range of addresses which should normally be examined. The third is the - modulo parameter: only addresses which are 0 module this value relative - to the first address of the range are actually considered. - probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. - A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for - the ISA bus, -1 for any I2C bus), the second is the address. These - addresses are also probed, as if they were in the 'normal' list. - probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END - values. - A list of triples. The first value is a bus number (SENSORS_ISA_BUS for - the ISA bus, -1 for any I2C bus), the second and third are addresses. - These form an inclusive range of addresses that are also probed, as - if they were in the 'normal' list. - ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values. - A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for - the ISA bus, -1 for any I2C bus), the second is the I2C address. These - addresses are never probed. This parameter overrules 'normal' and - 'probe', but not the 'force' lists. - ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END - values. - A list of triples. The first value is a bus number (SENSORS_ISA_BUS for - the ISA bus, -1 for any I2C bus), the second and third are addresses. - These form an inclusive range of I2C addresses that are never probed. - This parameter overrules 'normal' and 'probe', but not the 'force' lists. - force_data: insmod parameters. A list, ending with an element of which - the force field is NULL. -*/ -struct i2c_address_data { - unsigned short *normal_i2c; - unsigned short *normal_i2c_range; - unsigned int *normal_isa; - unsigned int *normal_isa_range; - unsigned short *probe; - unsigned short *probe_range; - unsigned short *ignore; - unsigned short *ignore_range; - struct i2c_force_data *forces; -}; - -/* Internal numbers to terminate lists */ -#define SENSORS_I2C_END 0xfffe -#define SENSORS_ISA_END 0xfffefffe - -/* The numbers to use to set an ISA or I2C bus address */ -#define SENSORS_ISA_BUS 9191 -#define SENSORS_ANY_I2C_BUS 0xffff - -/* The length of the option lists */ -#define SENSORS_MAX_OPTS 48 - -/* Default fill of many variables */ -#define SENSORS_DEFAULTS {SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ - SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END} - -/* This is ugly. We need to evaluate SENSORS_MAX_OPTS before it is - stringified */ -#define SENSORS_MODPARM_AUX1(x) "1-" #x "h" -#define SENSORS_MODPARM_AUX(x) SENSORS_MODPARM_AUX1(x) -#define SENSORS_MODPARM SENSORS_MODPARM_AUX(SENSORS_MAX_OPTS) - -/* SENSORS_MODULE_PARM creates a module parameter, and puts it in the - module header */ -#define SENSORS_MODULE_PARM(var,desc) \ - static unsigned short var[SENSORS_MAX_OPTS] = SENSORS_DEFAULTS; \ - MODULE_PARM(var,SENSORS_MODPARM); \ - MODULE_PARM_DESC(var,desc) - -/* SENSORS_MODULE_PARM creates a 'force_*' module parameter, and puts it in - the module header */ -#define SENSORS_MODULE_PARM_FORCE(name) \ - SENSORS_MODULE_PARM(force_ ## name, \ - "List of adapter,address pairs which are unquestionably" \ - " assumed to contain a `" # name "' chip") - - -/* This defines several insmod variables, and the addr_data structure */ -#define SENSORS_INSMOD \ - SENSORS_MODULE_PARM(probe, \ - "List of adapter,address pairs to scan additionally"); \ - SENSORS_MODULE_PARM(probe_range, \ - "List of adapter,start-addr,end-addr triples to scan " \ - "additionally"); \ - SENSORS_MODULE_PARM(ignore, \ - "List of adapter,address pairs not to scan"); \ - SENSORS_MODULE_PARM(ignore_range, \ - "List of adapter,start-addr,end-addr triples not to " \ - "scan"); \ - static struct i2c_address_data addr_data = \ - {normal_i2c, normal_i2c_range, \ - normal_isa, normal_isa_range, \ - probe, probe_range, \ - ignore, ignore_range, \ - forces} - -/* The following functions create an enum with the chip names as elements. - The first element of the enum is any_chip. These are the only macros - a module will want to use. */ - -#define SENSORS_INSMOD_0 \ - enum chips { any_chip }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - static struct i2c_force_data forces[] = {{force,any_chip},{NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_1(chip1) \ - enum chips { any_chip, chip1 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - static struct i2c_force_data forces[] = {{force,any_chip},\ - {force_ ## chip1,chip1}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_2(chip1,chip2) \ - enum chips { any_chip, chip1, chip2 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_3(chip1,chip2,chip3) \ - enum chips { any_chip, chip1, chip2, chip3 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_4(chip1,chip2,chip3,chip4) \ - enum chips { any_chip, chip1, chip2, chip3, chip4 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - SENSORS_MODULE_PARM_FORCE(chip4); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {force_ ## chip4,chip4}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_5(chip1,chip2,chip3,chip4,chip5) \ - enum chips { any_chip, chip1, chip2, chip3, chip4, chip5 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - SENSORS_MODULE_PARM_FORCE(chip4); \ - SENSORS_MODULE_PARM_FORCE(chip5); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {force_ ## chip4,chip4}, \ - {force_ ## chip5,chip5}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_6(chip1,chip2,chip3,chip4,chip5,chip6) \ - enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - SENSORS_MODULE_PARM_FORCE(chip4); \ - SENSORS_MODULE_PARM_FORCE(chip5); \ - SENSORS_MODULE_PARM_FORCE(chip6); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {force_ ## chip4,chip4}, \ - {force_ ## chip5,chip5}, \ - {force_ ## chip6,chip6}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_7(chip1,chip2,chip3,chip4,chip5,chip6,chip7) \ - enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - SENSORS_MODULE_PARM_FORCE(chip4); \ - SENSORS_MODULE_PARM_FORCE(chip5); \ - SENSORS_MODULE_PARM_FORCE(chip6); \ - SENSORS_MODULE_PARM_FORCE(chip7); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {force_ ## chip4,chip4}, \ - {force_ ## chip5,chip5}, \ - {force_ ## chip6,chip6}, \ - {force_ ## chip7,chip7}, \ - {NULL}}; \ - SENSORS_INSMOD - -#define SENSORS_INSMOD_8(chip1,chip2,chip3,chip4,chip5,chip6,chip7,chip8) \ - enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8 }; \ - SENSORS_MODULE_PARM(force, \ - "List of adapter,address pairs to boldly assume " \ - "to be present"); \ - SENSORS_MODULE_PARM_FORCE(chip1); \ - SENSORS_MODULE_PARM_FORCE(chip2); \ - SENSORS_MODULE_PARM_FORCE(chip3); \ - SENSORS_MODULE_PARM_FORCE(chip4); \ - SENSORS_MODULE_PARM_FORCE(chip5); \ - SENSORS_MODULE_PARM_FORCE(chip6); \ - SENSORS_MODULE_PARM_FORCE(chip7); \ - SENSORS_MODULE_PARM_FORCE(chip8); \ - static struct i2c_force_data forces[] = {{force,any_chip}, \ - {force_ ## chip1,chip1}, \ - {force_ ## chip2,chip2}, \ - {force_ ## chip3,chip3}, \ - {force_ ## chip4,chip4}, \ - {force_ ## chip5,chip5}, \ - {force_ ## chip6,chip6}, \ - {force_ ## chip7,chip7}, \ - {force_ ## chip8,chip8}, \ - {NULL}}; \ - SENSORS_INSMOD - -typedef int i2c_found_addr_proc(struct i2c_adapter *adapter, - int addr, int kind); - -/* Detect function. It iterates over all possible addresses itself. For - SMBus addresses, it will only call found_proc if some client is connected - to the SMBus (unless a 'force' matched); for ISA detections, this is not - done. */ -extern int i2c_detect(struct i2c_adapter *adapter, - struct i2c_address_data *address_data, - i2c_found_addr_proc * found_proc); - - -/* This macro is used to scale user-input to sensible values in almost all - chip drivers. */ -static inline int SENSORS_LIMIT(long value, long low, long high) -{ - if (value < low) - return low; - else if (value > high) - return high; - else - return value; -} - - -/* The maximum length of the prefix */ -#define SENSORS_PREFIX_MAX 20 - -/* Sysctl IDs */ -#ifdef DEV_HWMON -#define DEV_SENSORS DEV_HWMON -#else /* ndef DEV_HWMOM */ -#define DEV_SENSORS 2 /* The id of the lm_sensors directory within the - dev table */ -#endif /* def DEV_HWMON */ - -#define SENSORS_CHIPS 1 -struct i2c_chips_data { - int sysctl_id; - char name[SENSORS_PREFIX_MAX + 13]; -}; - -#endif /* def _LINUX_I2C_PROC_H */ - diff -Nru a/include/linux/i2c-sensor.h b/include/linux/i2c-sensor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/linux/i2c-sensor.h Wed Apr 2 16:00:46 2003 @@ -0,0 +1,373 @@ +/* + i2c-sensor.h - Part of the i2c package + was originally sensors.h - Part of lm_sensors, Linux kernel modules + for hardware monitoring + Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _LINUX_I2C_SENSOR_H +#define _LINUX_I2C_SENSOR_H + +#include <linux/sysctl.h> + +/* The type of callback functions used in sensors_{proc,sysctl}_real */ +typedef void (*i2c_real_callback) (struct i2c_client * client, + int operation, int ctl_name, + int *nrels_mag, long *results); + +/* Values for the operation field in the above function type */ +#define SENSORS_PROC_REAL_INFO 1 +#define SENSORS_PROC_REAL_READ 2 +#define SENSORS_PROC_REAL_WRITE 3 + +/* A structure containing detect information. + Force variables overrule all other variables; they force a detection on + that place. If a specific chip is given, the module blindly assumes this + chip type is present; if a general force (kind == 0) is given, the module + will still try to figure out what type of chip is present. This is useful + if for some reasons the detect for SMBus or ISA address space filled + fails. + probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the address. + kind: The kind of chip. 0 equals any chip. +*/ +struct i2c_force_data { + unsigned short *force; + unsigned short kind; +}; + +/* A structure containing the detect information. + normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. Terminated by + SENSORS_I2C_END + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. + A list of ISA addresses which should normally be examined. + normal_isa_range: filled in by the module writer. Terminated by + SENSORS_ISA_END + A list of triples. The first two elements are ISA addresses, being an + range of addresses which should normally be examined. The third is the + modulo parameter: only addresses which are 0 module this value relative + to the first address of the range are actually considered. + probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the address. These + addresses are also probed, as if they were in the 'normal' list. + probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of addresses that are also probed, as + if they were in the 'normal' list. + ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the I2C address. These + addresses are never probed. This parameter overrules 'normal' and + 'probe', but not the 'force' lists. + ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + force_data: insmod parameters. A list, ending with an element of which + the force field is NULL. +*/ +struct i2c_address_data { + unsigned short *normal_i2c; + unsigned short *normal_i2c_range; + unsigned int *normal_isa; + unsigned int *normal_isa_range; + unsigned short *probe; + unsigned short *probe_range; + unsigned short *ignore; + unsigned short *ignore_range; + struct i2c_force_data *forces; +}; + +/* Internal numbers to terminate lists */ +#define SENSORS_I2C_END 0xfffe +#define SENSORS_ISA_END 0xfffefffe + +/* The numbers to use to set an ISA or I2C bus address */ +#define SENSORS_ISA_BUS 9191 +#define SENSORS_ANY_I2C_BUS 0xffff + +/* The length of the option lists */ +#define SENSORS_MAX_OPTS 48 + +/* Default fill of many variables */ +#define SENSORS_DEFAULTS {SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \ + SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END} + +/* This is ugly. We need to evaluate SENSORS_MAX_OPTS before it is + stringified */ +#define SENSORS_MODPARM_AUX1(x) "1-" #x "h" +#define SENSORS_MODPARM_AUX(x) SENSORS_MODPARM_AUX1(x) +#define SENSORS_MODPARM SENSORS_MODPARM_AUX(SENSORS_MAX_OPTS) + +/* SENSORS_MODULE_PARM creates a module parameter, and puts it in the + module header */ +#define SENSORS_MODULE_PARM(var,desc) \ + static unsigned short var[SENSORS_MAX_OPTS] = SENSORS_DEFAULTS; \ + MODULE_PARM(var,SENSORS_MODPARM); \ + MODULE_PARM_DESC(var,desc) + +/* SENSORS_MODULE_PARM creates a 'force_*' module parameter, and puts it in + the module header */ +#define SENSORS_MODULE_PARM_FORCE(name) \ + SENSORS_MODULE_PARM(force_ ## name, \ + "List of adapter,address pairs which are unquestionably" \ + " assumed to contain a `" # name "' chip") + + +/* This defines several insmod variables, and the addr_data structure */ +#define SENSORS_INSMOD \ + SENSORS_MODULE_PARM(probe, \ + "List of adapter,address pairs to scan additionally"); \ + SENSORS_MODULE_PARM(probe_range, \ + "List of adapter,start-addr,end-addr triples to scan " \ + "additionally"); \ + SENSORS_MODULE_PARM(ignore, \ + "List of adapter,address pairs not to scan"); \ + SENSORS_MODULE_PARM(ignore_range, \ + "List of adapter,start-addr,end-addr triples not to " \ + "scan"); \ + static struct i2c_address_data addr_data = \ + {normal_i2c, normal_i2c_range, \ + normal_isa, normal_isa_range, \ + probe, probe_range, \ + ignore, ignore_range, \ + forces} + +/* The following functions create an enum with the chip names as elements. + The first element of the enum is any_chip. These are the only macros + a module will want to use. */ + +#define SENSORS_INSMOD_0 \ + enum chips { any_chip }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + static struct i2c_force_data forces[] = {{force,any_chip},{NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_1(chip1) \ + enum chips { any_chip, chip1 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + static struct i2c_force_data forces[] = {{force,any_chip},\ + {force_ ## chip1,chip1}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_2(chip1,chip2) \ + enum chips { any_chip, chip1, chip2 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_3(chip1,chip2,chip3) \ + enum chips { any_chip, chip1, chip2, chip3 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_4(chip1,chip2,chip3,chip4) \ + enum chips { any_chip, chip1, chip2, chip3, chip4 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + SENSORS_MODULE_PARM_FORCE(chip4); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {force_ ## chip4,chip4}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_5(chip1,chip2,chip3,chip4,chip5) \ + enum chips { any_chip, chip1, chip2, chip3, chip4, chip5 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + SENSORS_MODULE_PARM_FORCE(chip4); \ + SENSORS_MODULE_PARM_FORCE(chip5); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {force_ ## chip4,chip4}, \ + {force_ ## chip5,chip5}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_6(chip1,chip2,chip3,chip4,chip5,chip6) \ + enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + SENSORS_MODULE_PARM_FORCE(chip4); \ + SENSORS_MODULE_PARM_FORCE(chip5); \ + SENSORS_MODULE_PARM_FORCE(chip6); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {force_ ## chip4,chip4}, \ + {force_ ## chip5,chip5}, \ + {force_ ## chip6,chip6}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_7(chip1,chip2,chip3,chip4,chip5,chip6,chip7) \ + enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + SENSORS_MODULE_PARM_FORCE(chip4); \ + SENSORS_MODULE_PARM_FORCE(chip5); \ + SENSORS_MODULE_PARM_FORCE(chip6); \ + SENSORS_MODULE_PARM_FORCE(chip7); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {force_ ## chip4,chip4}, \ + {force_ ## chip5,chip5}, \ + {force_ ## chip6,chip6}, \ + {force_ ## chip7,chip7}, \ + {NULL}}; \ + SENSORS_INSMOD + +#define SENSORS_INSMOD_8(chip1,chip2,chip3,chip4,chip5,chip6,chip7,chip8) \ + enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8 }; \ + SENSORS_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + SENSORS_MODULE_PARM_FORCE(chip1); \ + SENSORS_MODULE_PARM_FORCE(chip2); \ + SENSORS_MODULE_PARM_FORCE(chip3); \ + SENSORS_MODULE_PARM_FORCE(chip4); \ + SENSORS_MODULE_PARM_FORCE(chip5); \ + SENSORS_MODULE_PARM_FORCE(chip6); \ + SENSORS_MODULE_PARM_FORCE(chip7); \ + SENSORS_MODULE_PARM_FORCE(chip8); \ + static struct i2c_force_data forces[] = {{force,any_chip}, \ + {force_ ## chip1,chip1}, \ + {force_ ## chip2,chip2}, \ + {force_ ## chip3,chip3}, \ + {force_ ## chip4,chip4}, \ + {force_ ## chip5,chip5}, \ + {force_ ## chip6,chip6}, \ + {force_ ## chip7,chip7}, \ + {force_ ## chip8,chip8}, \ + {NULL}}; \ + SENSORS_INSMOD + +typedef int i2c_found_addr_proc(struct i2c_adapter *adapter, + int addr, int kind); + +/* Detect function. It iterates over all possible addresses itself. For + SMBus addresses, it will only call found_proc if some client is connected + to the SMBus (unless a 'force' matched); for ISA detections, this is not + done. */ +extern int i2c_detect(struct i2c_adapter *adapter, + struct i2c_address_data *address_data, + i2c_found_addr_proc * found_proc); + + +/* This macro is used to scale user-input to sensible values in almost all + chip drivers. */ +static inline int SENSORS_LIMIT(long value, long low, long high) +{ + if (value < low) + return low; + else if (value > high) + return high; + else + return value; +} + + +/* The maximum length of the prefix */ +#define SENSORS_PREFIX_MAX 20 + +/* Sysctl IDs */ +#ifdef DEV_HWMON +#define DEV_SENSORS DEV_HWMON +#else /* ndef DEV_HWMOM */ +#define DEV_SENSORS 2 /* The id of the lm_sensors directory within the + dev table */ +#endif /* def DEV_HWMON */ + +#define SENSORS_CHIPS 1 +struct i2c_chips_data { + int sysctl_id; + char name[SENSORS_PREFIX_MAX + 13]; +}; + +#endif /* def _LINUX_I2C_SENSOR_H */ + ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.11, 2003/04/02 13:54:45-08:00, greg@kroah.com i2c: remove all proc code from the i2c core, as it's no longer needed. drivers/i2c/i2c-core.c | 187 ------------------------------------------------- 1 files changed, 1 insertion(+), 186 deletions(-) diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c Wed Apr 2 16:00:37 2003 +++ b/drivers/i2c/i2c-core.c Wed Apr 2 16:00:37 2003 @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/proc_fs.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/seq_file.h> @@ -46,15 +45,6 @@ /**** debug level */ static int i2c_debug; -#ifdef CONFIG_PROC_FS -static int i2cproc_register(struct i2c_adapter *adap, int bus); -static void i2cproc_remove(int bus); -#else -# define i2cproc_register(adap, bus) 0 -# define i2cproc_remove(bus) do { } while (0) -#endif /* CONFIG_PROC_FS */ - - int i2c_device_probe(struct device *dev) { return -ENODEV; @@ -98,10 +88,6 @@ goto out_unlock; } - res = i2cproc_register(adap, i); - if (res) - goto out_unlock; - adapters[i] = adap; init_MUTEX(&adap->bus); @@ -180,8 +166,6 @@ } } - i2cproc_remove(i); - /* clean up the sysfs representation */ device_unregister(&adap->dev); @@ -495,173 +479,6 @@ return 0; } -#ifdef CONFIG_PROC_FS -/* This function generates the output for /proc/bus/i2c-? */ -static ssize_t i2cproc_bus_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_dentry->d_inode; - char *kbuf; - struct i2c_client *client; - int i,j,k,order_nr,len=0; - size_t len_total; - int order[I2C_CLIENT_MAX]; -#define OUTPUT_LENGTH_PER_LINE 70 - - len_total = file->f_pos + count; - if (len_total > (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE) ) - /* adjust to maximum file size */ - len_total = (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE); - for (i = 0; i < I2C_ADAP_MAX; i++) - if (adapters[i]->inode == inode->i_ino) { - /* We need a bit of slack in the kernel buffer; this makes the - sprintf safe. */ - if (! (kbuf = kmalloc(len_total + - OUTPUT_LENGTH_PER_LINE, - GFP_KERNEL))) - return -ENOMEM; - /* Order will hold the indexes of the clients - sorted by address */ - order_nr=0; - for (j = 0; j < I2C_CLIENT_MAX; j++) { - if ((client = adapters[i]->clients[j]) && - (client->driver->id != I2C_DRIVERID_I2CDEV)) { - for(k = order_nr; - (k > 0) && - adapters[i]->clients[order[k-1]]-> - addr > client->addr; - k--) - order[k] = order[k-1]; - order[k] = j; - order_nr++; - } - } - - - for (j = 0; (j < order_nr) && (len < len_total); j++) { - client = adapters[i]->clients[order[j]]; - len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", - client->addr, - client->dev.name, - client->driver->name); - } - len = len - file->f_pos; - if (len > count) - len = count; - if (len < 0) - len = 0; - if (copy_to_user (buf,kbuf+file->f_pos, len)) { - kfree(kbuf); - return -EFAULT; - } - file->f_pos += len; - kfree(kbuf); - return len; - } - return -ENOENT; -} - -static struct file_operations i2cproc_operations = { - .read = i2cproc_bus_read, -}; - -/* This function generates the output for /proc/bus/i2c */ -static int bus_i2c_show(struct seq_file *s, void *p) -{ - int i; - - down(&core_lists); - for (i = 0; i < I2C_ADAP_MAX; i++) { - struct i2c_adapter *adapter = adapters[i]; - - if (!adapter) - continue; - - seq_printf(s, "i2c-%d\t", i); - - if (adapter->algo->smbus_xfer) { - if (adapter->algo->master_xfer) - seq_printf(s, "smbus/i2c"); - else - seq_printf(s, "smbus "); - } else if (adapter->algo->master_xfer) - seq_printf(s ,"i2c "); - else - seq_printf(s, "dummy "); - - seq_printf(s, "\t%-32s\t%-32s\n", - adapter->dev.name, adapter->algo->name); - } - up(&core_lists); - - return 0; -} - -static int bus_i2c_open(struct inode *inode, struct file *file) -{ - return single_open(file, bus_i2c_show, NULL); -} - -static struct file_operations bus_i2c_fops = { - .open = bus_i2c_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - }; - -static int i2cproc_register(struct i2c_adapter *adap, int bus) -{ - struct proc_dir_entry *proc_entry; - char name[8]; - - sprintf(name, "i2c-%d", bus); - - proc_entry = create_proc_entry(name, 0, proc_bus); - if (!proc_entry) - goto fail; - - proc_entry->proc_fops = &i2cproc_operations; - proc_entry->owner = adap->owner; - adap->inode = proc_entry->low_ino; - return 0; - fail: - printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/%s\n", name); - return -ENOENT; -} - -static void i2cproc_remove(int bus) -{ - char name[8]; - - sprintf(name,"i2c-%d", bus); - remove_proc_entry(name, proc_bus); -} - -static int __init i2cproc_init(void) -{ - struct proc_dir_entry *proc_bus_i2c; - - proc_bus_i2c = create_proc_entry("i2c", 0, proc_bus); - if (!proc_bus_i2c) - goto fail; - proc_bus_i2c->proc_fops = &bus_i2c_fops; - proc_bus_i2c->owner = THIS_MODULE; - return 0; - - fail: - printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); - return -ENOENT; -} - -static void __exit i2cproc_cleanup(void) -{ - remove_proc_entry("i2c",proc_bus); -} -#else -static int __init i2cproc_init(void) { return 0; } -static void __exit i2cproc_cleanup(void) { } -#endif /* CONFIG_PROC_FS */ - /* match always succeeds, as we want the probe() to tell if we really accept this match */ static int i2c_device_match(struct device *dev, struct device_driver *drv) { @@ -676,13 +493,11 @@ static int __init i2c_init(void) { - bus_register(&i2c_bus_type); - return i2cproc_init(); + return bus_register(&i2c_bus_type); } static void __exit i2c_exit(void) { - i2cproc_cleanup(); bus_unregister(&i2c_bus_type); } ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.12, 2003/04/02 14:07:40-08:00, azarah@gentoo.org [PATCH] i2c: w83781d i2c driver updated for 2.5.66-bk4 (with sysfs support, empty tree) This should have a working w83781d driver updated for 2.5.66-bk4. Currently sysfs support is working, and are according to the spec (sensors-sysfs) in the 'lm sensors sysfs file structure' thread. Thus I used 'temp_input[1-3]', as there was not final decision on having them temp_input[0-2] as well, for example. drivers/i2c/chips/Kconfig | 14 drivers/i2c/chips/w83781d.c | 1884 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-vid.h | 62 + 3 files changed, 1960 insertions(+) diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:28 2003 +++ b/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:28 2003 @@ -36,6 +36,20 @@ You will also need the latest user-space utilties: you can find them in the lm_sensors package, which you can download at http://www.lm-sensors.nu + +config SENSORS_W83781D + tristate " Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" + depends on I2C && I2C_PROC + help + If you say yes here you get support for the Winbond W8378x series + of sensor chips: the W83781D, W83782D, W83783S and W83682HF, + and the similar Asus AS99127F. This + can also be built as a module which can be inserted and removed + while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu config SENSORS_VIA686A tristate " VIA686A" diff -Nru a/drivers/i2c/chips/w83781d.c b/drivers/i2c/chips/w83781d.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/chips/w83781d.c Wed Apr 2 16:00:28 2003 @@ -0,0 +1,1884 @@ +/* + w83781d.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, + Philip Edelbrock <phil@netroedge.com>, + and Mark Studebaker <mdsxyz123@yahoo.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + as99127f 7 3 1? 3 0x30 0x12c3 yes no + asb100 "bach" (type_name = as99127f) 0x30 0x0694 yes no + w83781d 7 3 0 3 0x10 0x5ca3 yes yes + w83627hf 9 3 2 3 0x20 0x5ca3 yes yes(LPC) + w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes + w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) + +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/i2c-proc.h> +#include <linux/i2c-vid.h> +#include <asm/io.h> + +/* RT Table support #defined so we can take it out if it gets bothersome */ +#define W83781D_RT 1 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf); +SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +static int init = 1; +MODULE_PARM(init, "i"); +MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); + +/* Constants specified below */ + +/* Length of ISA address segment */ +#define W83781D_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define W83781D_ADDR_REG_OFFSET 5 +#define W83781D_DATA_REG_OFFSET 6 + +/* The W83781D registers */ +/* The W83782D registers for nr=7,8 are in bank 5 */ +#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + +#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) +#define W83781D_REG_FAN(nr) (0x27 + (nr)) + +#define W83781D_REG_BANK 0x4E +#define W83781D_REG_TEMP2_CONFIG 0x152 +#define W83781D_REG_TEMP3_CONFIG 0x252 +#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ + ((nr == 2) ? (0x0150) : \ + (0x27))) +#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ + ((nr == 2) ? (0x153) : \ + (0x3A))) +#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ + ((nr == 2) ? (0x155) : \ + (0x39))) + +#define W83781D_REG_CONFIG 0x40 +#define W83781D_REG_ALARM1 0x41 +#define W83781D_REG_ALARM2 0x42 +#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ + +#define W83781D_REG_IRQ 0x4C +#define W83781D_REG_BEEP_CONFIG 0x4D +#define W83781D_REG_BEEP_INTS1 0x56 +#define W83781D_REG_BEEP_INTS2 0x57 +#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ + +#define W83781D_REG_VID_FANDIV 0x47 + +#define W83781D_REG_CHIPID 0x49 +#define W83781D_REG_WCHIPID 0x58 +#define W83781D_REG_CHIPMAN 0x4F +#define W83781D_REG_PIN 0x4B + +/* 782D/783S only */ +#define W83781D_REG_VBAT 0x5D + +/* PWM 782D (1-4) and 783S (1-2) only */ +#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ + /* on which is which; */ +#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ + /* However 782d is probably wrong. */ +#define W83781D_REG_PWM3 0x5E +#define W83781D_REG_PWM4 0x5F +#define W83781D_REG_PWMCLK12 0x5C +#define W83781D_REG_PWMCLK34 0x45C +static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, + W83781D_REG_PWM3, W83781D_REG_PWM4 +}; + +#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1]) + +#define W83781D_REG_I2C_ADDR 0x48 +#define W83781D_REG_I2C_SUBADDR 0x4A + +/* The following are undocumented in the data sheets however we + received the information in an email from Winbond tech support */ +/* Sensor selection - not on 781d */ +#define W83781D_REG_SCFG1 0x5D +static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; + +#define W83781D_REG_SCFG2 0x59 +static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; + +#define W83781D_DEFAULT_BETA 3435 + +/* RT Table registers */ +#define W83781D_REG_RT_IDX 0x50 +#define W83781D_REG_RT_VAL 0x51 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +static inline u8 +FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ + ((val) == 255 ? 0 : \ + 1350000 / ((val) * (div)))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val / 10) < 0 ? (((val / 10) - 5) / 10) : \ + ((val / 10) + 5) / 10), 0, 255)) +#define TEMP_FROM_REG(val) ((((val ) > 0x80 ? (val) - 0x100 : (val)) * 10) * 10) + +#define TEMP_ADD_TO_REG(val) (SENSORS_LIMIT(((((val / 10) + 2) / 5) << 7),\ + 0, 0xffff)) +#define TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 5) * 10) + +#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val / 10) + 2)*4)/10) \ + << 7), 0, 0xffff)) +#define AS99127_TEMP_ADD_FROM_REG(val) (((((val) >> 7) * 10) / 4) * 10) + +#define ALARMS_FROM_REG(val) (val) +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) +#define BEEP_MASK_FROM_REG(val) (val) +#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) + +#define BEEP_ENABLE_TO_REG(val) ((val) ? 1 : 0) +#define BEEP_ENABLE_FROM_REG(val) ((val) ? 1 : 0) + +#define DIV_FROM_REG(val) (1 << (val)) + +static inline u8 +DIV_TO_REG(long val, enum chips type) +{ + int i; + val = SENSORS_LIMIT(val, 1, + ((type == w83781d + || type == as99127f) ? 8 : 128)) >> 1; + for (i = 0; i < 6; i++) { + if (val == 0) + break; + val >>= 1; + } + return ((u8) i); +} + +/* Initial limits */ +#define W83781D_INIT_IN_0 (vid == 3500 ? 280 : vid / 10) +#define W83781D_INIT_IN_1 (vid == 3500 ? 280 : vid / 10) +#define W83781D_INIT_IN_2 330 +#define W83781D_INIT_IN_3 (((500) * 100) / 168) +#define W83781D_INIT_IN_4 (((1200) * 10) / 38) +#define W83781D_INIT_IN_5 (((-1200) * -604) / 2100) +#define W83781D_INIT_IN_6 (((-500) * -604) / 909) +#define W83781D_INIT_IN_7 (((500) * 100) / 168) +#define W83781D_INIT_IN_8 300 +/* Initial limits for 782d/783s negative voltages */ +/* Note level shift. Change min/max below if you change these. */ +#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514) +#define W83782D_INIT_IN_6 ((( (-500) + 771) * 100)/314) + +#define W83781D_INIT_IN_PERCENTAGE 10 +#define W83781D_INIT_IN_MIN(val) (val - val * W83781D_INIT_IN_PERCENTAGE / 100) +#define W83781D_INIT_IN_MAX(val) (val + val * W83781D_INIT_IN_PERCENTAGE / 100) + +#define W83781D_INIT_IN_MIN_0 W83781D_INIT_IN_MIN(W83781D_INIT_IN_0) +#define W83781D_INIT_IN_MAX_0 W83781D_INIT_IN_MAX(W83781D_INIT_IN_0) +#define W83781D_INIT_IN_MIN_1 W83781D_INIT_IN_MIN(W83781D_INIT_IN_1) +#define W83781D_INIT_IN_MAX_1 W83781D_INIT_IN_MAX(W83781D_INIT_IN_1) +#define W83781D_INIT_IN_MIN_2 W83781D_INIT_IN_MIN(W83781D_INIT_IN_2) +#define W83781D_INIT_IN_MAX_2 W83781D_INIT_IN_MAX(W83781D_INIT_IN_2) +#define W83781D_INIT_IN_MIN_3 W83781D_INIT_IN_MIN(W83781D_INIT_IN_3) +#define W83781D_INIT_IN_MAX_3 W83781D_INIT_IN_MAX(W83781D_INIT_IN_3) +#define W83781D_INIT_IN_MIN_4 W83781D_INIT_IN_MIN(W83781D_INIT_IN_4) +#define W83781D_INIT_IN_MAX_4 W83781D_INIT_IN_MAX(W83781D_INIT_IN_4) +#define W83781D_INIT_IN_MIN_5 W83781D_INIT_IN_MIN(W83781D_INIT_IN_5) +#define W83781D_INIT_IN_MAX_5 W83781D_INIT_IN_MAX(W83781D_INIT_IN_5) +#define W83781D_INIT_IN_MIN_6 W83781D_INIT_IN_MIN(W83781D_INIT_IN_6) +#define W83781D_INIT_IN_MAX_6 W83781D_INIT_IN_MAX(W83781D_INIT_IN_6) +#define W83781D_INIT_IN_MIN_7 W83781D_INIT_IN_MIN(W83781D_INIT_IN_7) +#define W83781D_INIT_IN_MAX_7 W83781D_INIT_IN_MAX(W83781D_INIT_IN_7) +#define W83781D_INIT_IN_MIN_8 W83781D_INIT_IN_MIN(W83781D_INIT_IN_8) +#define W83781D_INIT_IN_MAX_8 W83781D_INIT_IN_MAX(W83781D_INIT_IN_8) + +/* Initial limits for 782d/783s negative voltages */ +/* These aren't direct multiples because of level shift */ +/* Beware going negative - check */ +#define W83782D_INIT_IN_MIN_5_TMP \ + (((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_5 \ + ((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0) +#define W83782D_INIT_IN_MAX_5 \ + (((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_6_TMP \ + ((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) +#define W83782D_INIT_IN_MIN_6 \ + ((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0) +#define W83782D_INIT_IN_MAX_6 \ + ((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) + +#define W83781D_INIT_FAN_MIN_1 3000 +#define W83781D_INIT_FAN_MIN_2 3000 +#define W83781D_INIT_FAN_MIN_3 3000 + +/* temp = value / 100 */ +#define W83781D_INIT_TEMP_OVER 6000 +#define W83781D_INIT_TEMP_HYST 12700 /* must be 127 for ALARM to work */ +#define W83781D_INIT_TEMP2_OVER 6000 +#define W83781D_INIT_TEMP2_HYST 5000 +#define W83781D_INIT_TEMP3_OVER 6000 +#define W83781D_INIT_TEMP3_HYST 5000 + +/* There are some complications in a module like this. First off, W83781D chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + W83781D chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the W83781D at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered W83781D, we need to keep some data in memory. That + data is pointed to by w83781d_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new w83781d client is + allocated. */ +struct w83781d_data { + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + struct i2c_client *lm75; /* for secondary I2C addresses */ + /* pointer to array of 2 subclients */ + + u8 in[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; + u8 temp_min; /* Register value */ + u8 temp_max; /* Register value */ + u16 temp_add[2]; /* Register value */ + u16 temp_max_add[2]; /* Register value */ + u16 temp_min_add[2]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u32 beep_mask; /* Register encoding, combined */ + u8 beep_enable; /* Boolean */ + u8 pwm[4]; /* Register value */ + u8 pwmenable[4]; /* Boolean */ + u16 sens[3]; /* 782D/783S only. + 1 = pentium diode; 2 = 3904 diode; + 3000-5000 = thermistor beta. + Default = 3435. + Other Betas unimplemented */ +#ifdef W83781D_RT + u8 rt[3][32]; /* Register value */ +#endif + u8 vrm; +}; + +static int w83781d_attach_adapter(struct i2c_adapter *adapter); +static int w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int w83781d_detach_client(struct i2c_client *client); + +static int w83781d_read_value(struct i2c_client *client, u16 register); +static int w83781d_write_value(struct i2c_client *client, u16 register, + u16 value); +static void w83781d_update_client(struct i2c_client *client); +static void w83781d_init_client(struct i2c_client *client); + +static inline u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +static struct i2c_driver w83781d_driver = { + .owner = THIS_MODULE, + .name = "w83781d", + .id = I2C_DRIVERID_W83781D, + .flags = I2C_DF_NOTIFY, + .attach_adapter = w83781d_attach_adapter, + .detach_client = w83781d_detach_client, +}; + +/* following are the sysfs callback functions */ +#define show_in_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + \ + w83781d_update_client(client); \ + \ + return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr])); \ +} +show_in_reg(in); +show_in_reg(in_min); +show_in_reg(in_max); + +#define store_in_reg(REG, reg) \ +static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10); \ + data->in_##reg[nr] = IN_TO_REG(val); \ + w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \ + \ + return count; \ +} +store_in_reg(MIN, min); +store_in_reg(MAX, max); + +#define sysfs_in_offset(offset) \ +static ssize_t \ +show_regs_in_##offset (struct device *dev, char *buf) \ +{ \ + return show_in(dev, buf, 0x##offset); \ +} \ +static DEVICE_ATTR(in_input##offset, S_IRUGO, show_regs_in_##offset, NULL) + +#define sysfs_in_reg_offset(reg, offset) \ +static ssize_t show_regs_in_##reg##offset (struct device *dev, char *buf) \ +{ \ + return show_in_##reg (dev, buf, 0x##offset); \ +} \ +static ssize_t store_regs_in_##reg##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_in_##reg (dev, buf, count, 0x##offset); \ +} \ +static DEVICE_ATTR(in_##reg##offset, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset) + +#define sysfs_in_offsets(offset) \ +sysfs_in_offset(offset); \ +sysfs_in_reg_offset(min, offset); \ +sysfs_in_reg_offset(max, offset); + +sysfs_in_offsets(0); +sysfs_in_offsets(1); +sysfs_in_offsets(2); +sysfs_in_offsets(3); +sysfs_in_offsets(4); +sysfs_in_offsets(5); +sysfs_in_offsets(6); +sysfs_in_offsets(7); +sysfs_in_offsets(8); + +#define device_create_file_in(client, offset) \ +device_create_file(&client->dev, &dev_attr_in_input##offset); \ +device_create_file(&client->dev, &dev_attr_in_min##offset); \ +device_create_file(&client->dev, &dev_attr_in_max##offset); + +#define show_fan_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + \ + w83781d_update_client(client); \ + \ + return sprintf(buf,"%ld\n", \ + FAN_FROM_REG(data->reg[nr-1], (long)DIV_FROM_REG(data->fan_div[nr-1]))); \ +} +show_fan_reg(fan); +show_fan_reg(fan_min); + +static ssize_t +store_fan_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->fan_min[nr - 1] = + FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); + w83781d_write_value(client, W83781D_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + + return count; +} + +#define sysfs_fan_offset(offset) \ +static ssize_t show_regs_fan_##offset (struct device *dev, char *buf) \ +{ \ + return show_fan(dev, buf, 0x##offset); \ +} \ +static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_regs_fan_##offset, NULL) + +#define sysfs_fan_min_offset(offset) \ +static ssize_t show_regs_fan_min##offset (struct device *dev, char *buf) \ +{ \ + return show_fan_min(dev, buf, 0x##offset); \ +} \ +static ssize_t store_regs_fan_min##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_fan_min(dev, buf, count, 0x##offset); \ +} \ +static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, show_regs_fan_min##offset, store_regs_fan_min##offset) + +sysfs_fan_offset(1); +sysfs_fan_min_offset(1); +sysfs_fan_offset(2); +sysfs_fan_min_offset(2); +sysfs_fan_offset(3); +sysfs_fan_min_offset(3); + +#define device_create_file_fan(client, offset) \ +device_create_file(&client->dev, &dev_attr_fan_input##offset); \ +device_create_file(&client->dev, &dev_attr_fan_min##offset); \ + +#define show_temp_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + \ + w83781d_update_client(client); \ + \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + if (data->type == as99127f) { \ + return sprintf(buf,"%ld\n", \ + (long)AS99127_TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \ + } else { \ + return sprintf(buf,"%ld\n", \ + (long)TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \ + } \ + } else { /* TEMP1 */ \ + return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ + } \ +} +show_temp_reg(temp); +show_temp_reg(temp_min); +show_temp_reg(temp_max); + +#define store_temp_reg(REG, reg) \ +static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10); \ + \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + if (data->type == as99127f) \ + data->temp_##reg##_add[nr-2] = AS99127_TEMP_ADD_TO_REG(val); \ + else \ + data->temp_##reg##_add[nr-2] = TEMP_ADD_TO_REG(val); \ + \ + w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg##_add[nr-2]); \ + } else { /* TEMP1 */ \ + data->temp_##reg = TEMP_TO_REG(val); \ + w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg); \ + } \ + \ + return count; \ +} +store_temp_reg(OVER, min); +store_temp_reg(HYST, max); + +#define sysfs_temp_offset(offset) \ +static ssize_t \ +show_regs_temp_##offset (struct device *dev, char *buf) \ +{ \ + return show_temp(dev, buf, 0x##offset); \ +} \ +static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_regs_temp_##offset, NULL) + +#define sysfs_temp_reg_offset(reg, offset) \ +static ssize_t show_regs_temp_##reg##offset (struct device *dev, char *buf) \ +{ \ + return show_temp_##reg (dev, buf, 0x##offset); \ +} \ +static ssize_t store_regs_temp_##reg##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_temp_##reg (dev, buf, count, 0x##offset); \ +} \ +static DEVICE_ATTR(temp_##reg##offset, S_IRUGO| S_IWUSR, show_regs_temp_##reg##offset, store_regs_temp_##reg##offset) + +#define sysfs_temp_offsets(offset) \ +sysfs_temp_offset(offset); \ +sysfs_temp_reg_offset(min, offset); \ +sysfs_temp_reg_offset(max, offset); + +sysfs_temp_offsets(1); +sysfs_temp_offsets(2); +sysfs_temp_offsets(3); + +#define device_create_file_temp(client, offset) \ +device_create_file(&client->dev, &dev_attr_temp_input##offset); \ +device_create_file(&client->dev, &dev_attr_temp_max##offset); \ +device_create_file(&client->dev, &dev_attr_temp_min##offset); + +static ssize_t +show_vid_reg(struct device *dev, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} + +static +DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL) +#define device_create_file_vid(client) \ +device_create_file(&client->dev, &dev_attr_vid); +static ssize_t +show_vrm_reg(struct device *dev, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) data->vrm); +} + +static ssize_t +store_vrm_reg(struct device *dev, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + + return count; +} + +static +DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg) +#define device_create_file_vrm(client) \ +device_create_file(&client->dev, &dev_attr_vrm); +static ssize_t +show_alarms_reg(struct device *dev, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) ALARMS_FROM_REG(data->alarms)); +} + +static +DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL) +#define device_create_file_alarms(client) \ +device_create_file(&client->dev, &dev_attr_alarms); +#define show_beep_reg(REG, reg) \ +static ssize_t show_beep_##reg (struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + \ + w83781d_update_client(client); \ + \ + return sprintf(buf,"%ld\n", (long)BEEP_##REG##_FROM_REG(data->beep_##reg)); \ +} +show_beep_reg(ENABLE, enable); +show_beep_reg(MASK, mask); + +#define BEEP_ENABLE 0 /* Store beep_enable */ +#define BEEP_MASK 1 /* Store beep_mask */ + +static ssize_t +store_beep_reg(struct device *dev, const char *buf, size_t count, + int update_mask) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, val2; + + val = simple_strtoul(buf, NULL, 10); + + if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ + data->beep_mask = BEEP_MASK_TO_REG(val); + w83781d_write_value(client, W83781D_REG_BEEP_INTS1, + data->beep_mask & 0xff); + + if ((data->type != w83781d) && (data->type != as99127f)) { + w83781d_write_value(client, W83781D_REG_BEEP_INTS3, + ((data->beep_mask) >> 16) & 0xff); + } + + val2 = (data->beep_mask >> 8) & 0x7f; + } else { /* We are storing beep_enable */ + val2 = w83781d_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; + data->beep_enable = BEEP_ENABLE_TO_REG(val); + } + + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, + val2 | data->beep_enable << 7); + + return count; +} + +#define sysfs_beep(REG, reg) \ +static ssize_t show_regs_beep_##reg (struct device *dev, char *buf) \ +{ \ + return show_beep_##reg(dev, buf); \ +} \ +static ssize_t store_regs_beep_##reg (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_beep_reg(dev, buf, count, BEEP_##REG); \ +} \ +static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_regs_beep_##reg) + +sysfs_beep(ENABLE, enable); +sysfs_beep(MASK, mask); + +#define device_create_file_beep(client) \ +device_create_file(&client->dev, &dev_attr_beep_enable); \ +device_create_file(&client->dev, &dev_attr_beep_mask); + +/* w83697hf only has two fans */ +static ssize_t +show_fan_div_reg(struct device *dev, char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", + (long) DIV_FROM_REG(data->fan_div[nr - 1])); +} + +/* w83697hf only has two fans */ +static ssize_t +store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, old, old2, old3; + + val = simple_strtoul(buf, NULL, 10); + old = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + + data->fan_div[nr - 1] = DIV_TO_REG(val, data->type); + + /* w83781d and as99127f don't have extended divisor bits */ + if ((data->type != w83781d) && data->type != as99127f) { + old3 = w83781d_read_value(client, W83781D_REG_VBAT); + } + if (nr >= 3 && data->type != w83697hf) { + old2 = w83781d_read_value(client, W83781D_REG_PIN); + old2 = (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); + w83781d_write_value(client, W83781D_REG_PIN, old2); + + if ((data->type != w83781d) && (data->type != as99127f)) { + old3 = (old3 & 0x7f) | ((data->fan_div[2] & 0x04) << 5); + } + } + if (nr >= 2) { + old = (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); + + if ((data->type != w83781d) && (data->type != as99127f)) { + old3 = (old3 & 0xbf) | ((data->fan_div[1] & 0x04) << 4); + } + } + if (nr >= 1) { + old = (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); + w83781d_write_value(client, W83781D_REG_VID_FANDIV, old); + + if ((data->type != w83781d) && (data->type != as99127f)) { + old3 = (old3 & 0xdf) | ((data->fan_div[0] & 0x04) << 3); + w83781d_write_value(client, W83781D_REG_VBAT, old3); + } + } + + return count; +} + +#define sysfs_fan_div(offset) \ +static ssize_t show_regs_fan_div_##offset (struct device *dev, char *buf) \ +{ \ + return show_fan_div_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_fan_div_##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_fan_div_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, show_regs_fan_div_##offset, store_regs_fan_div_##offset) + +sysfs_fan_div(1); +sysfs_fan_div(2); +sysfs_fan_div(3); + +#define device_create_file_fan_div(client, offset) \ +device_create_file(&client->dev, &dev_attr_fan_div##offset); \ + +/* w83697hf only has two fans */ +static ssize_t +show_pwm_reg(struct device *dev, char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr - 1])); +} + +/* w83697hf only has two fans */ +static ssize_t +show_pwmenable_reg(struct device *dev, char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) data->pwmenable[nr - 1]); +} + +static ssize_t +store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + data->pwm[nr - 1] = PWM_TO_REG(val); + w83781d_write_value(client, W83781D_REG_PWM(nr), data->pwm[nr - 1]); + + return count; +} + +static ssize_t +store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, j, k; + + val = simple_strtoul(buf, NULL, 10); + + /* only PWM2 can be enabled/disabled */ + if (nr == 2) { + j = w83781d_read_value(client, W83781D_REG_PWMCLK12); + k = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + + if (val > 0) { + if (!(j & 0x08)) + w83781d_write_value(client, + W83781D_REG_PWMCLK12, + j | 0x08); + if (k & 0x10) + w83781d_write_value(client, + W83781D_REG_BEEP_CONFIG, + k & 0xef); + + data->pwmenable[1] = 1; + } else { + if (j & 0x08) + w83781d_write_value(client, + W83781D_REG_PWMCLK12, + j & 0xf7); + if (!(k & 0x10)) + w83781d_write_value(client, + W83781D_REG_BEEP_CONFIG, + j | 0x10); + + data->pwmenable[1] = 0; + } + } + + return count; +} + +#define sysfs_pwm(offset) \ +static ssize_t show_regs_pwm_##offset (struct device *dev, char *buf) \ +{ \ + return show_pwm_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_pwm_##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_pwm_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, show_regs_pwm_##offset, store_regs_pwm_##offset) + +#define sysfs_pwmenable(offset) \ +static ssize_t show_regs_pwmenable_##offset (struct device *dev, char *buf) \ +{ \ + return show_pwmenable_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_pwmenable_##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_pwmenable_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(pwm_enable##offset, S_IRUGO | S_IWUSR, show_regs_pwmenable_##offset, store_regs_pwmenable_##offset) + +sysfs_pwm(1); +sysfs_pwm(2); +sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */ +sysfs_pwm(3); +sysfs_pwm(4); + +#define device_create_file_pwm(client, offset) \ +device_create_file(&client->dev, &dev_attr_pwm##offset); \ + +#define device_create_file_pwmenable(client, offset) \ +device_create_file(&client->dev, &dev_attr_pwm_enable##offset); \ + +static ssize_t +show_sensor_reg(struct device *dev, char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + + w83781d_update_client(client); + + return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]); +} + +static ssize_t +store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, tmp; + + val = simple_strtoul(buf, NULL, 10); + + switch (val) { + case 1: /* PII/Celeron diode */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83781d_read_value(client, W83781D_REG_SCFG2); + w83781d_write_value(client, W83781D_REG_SCFG2, + tmp | BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case 2: /* 3904 */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83781d_read_value(client, W83781D_REG_SCFG2); + w83781d_write_value(client, W83781D_REG_SCFG2, + tmp & ~BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case W83781D_DEFAULT_BETA: /* thermistor */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp & ~BIT_SCFG1[nr - 1]); + data->sens[nr - 1] = val; + break; + default: + dev_err(&client->dev, + "Invalid sensor type %ld; must be 1, 2, or %d\n", + (long) val, W83781D_DEFAULT_BETA); + break; + } + + return count; +} + +#define sysfs_sensor(offset) \ +static ssize_t show_regs_sensor_##offset (struct device *dev, char *buf) \ +{ \ + return show_sensor_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_sensor_##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_sensor_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(sensor##offset, S_IRUGO | S_IWUSR, show_regs_sensor_##offset, store_regs_sensor_##offset) + +sysfs_sensor(1); +sysfs_sensor(2); +sysfs_sensor(3); + +#define device_create_file_sensor(client, offset) \ +device_create_file(&client->dev, &dev_attr_sensor##offset); \ + +#ifdef W83781D_RT +static ssize_t +show_rt_reg(struct device *dev, char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + int i, j = 0; + + w83781d_update_client(client); + + for (i = 0; i < 32; i++) { + if (i > 0) + j += sprintf(buf, " %ld", (long) data->rt[nr - 1][i]); + else + j += sprintf(buf, "%ld", (long) data->rt[nr - 1][i]); + } + j += sprintf(buf, "\n"); + + return j; +} + +static ssize_t +store_rt_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, i; + + for (i = 0; i < count; i++) { + val = simple_strtoul(buf + count, NULL, 10); + + /* fixme: no bounds checking 0-255 */ + data->rt[nr - 1][i] = val & 0xff; + w83781d_write_value(client, W83781D_REG_RT_IDX, i); + w83781d_write_value(client, W83781D_REG_RT_VAL, + data->rt[nr - 1][i]); + } + + return count; +} + +#define sysfs_rt(offset) \ +static ssize_t show_regs_rt_##offset (struct device *dev, char *buf) \ +{ \ + return show_rt_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_rt_##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + return store_rt_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(rt##offset, S_IRUGO | S_IWUSR, show_regs_rt_##offset, store_regs_rt_##offset) + +sysfs_rt(1); +sysfs_rt(2); +sysfs_rt(3); + +#define device_create_file_rt(client, offset) \ +device_create_file(&client->dev, &dev_attr_rt##offset); \ + +#endif /* ifdef W83781D_RT */ + +/* This function is called when: + * w83781d_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83781d_driver is still present) */ +static int +w83781d_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, w83781d_detect); +} + +static int +w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i = 0, val1 = 0, val2, id; + struct i2c_client *new_client; + struct w83781d_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + enum vendor { winbond, asus } vendid; + + if (!is_isa + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + if (is_isa) { + if (!request_region(address, W83781D_EXTENT, "w83781d")) + goto ERROR0; + release_region(address, W83781D_EXTENT); + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83781d_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof (struct i2c_client) + + sizeof (struct w83781d_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + memset(new_client, 0x00, sizeof (struct i2c_client) + + sizeof (struct w83781d_data)); + + data = (struct w83781d_data *) (new_client + 1); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &w83781d_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + /* The w8378?d may be stuck in some other bank than bank 0. This may + make reading other information impossible. Specify a force=... or + force_*=... parameter, and the Winbond will be reset to the right + bank. */ + if (kind < 0) { + if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) + goto ERROR1; + val1 = w83781d_read_value(new_client, W83781D_REG_BANK); + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + /* Check for Winbond or Asus ID if in bank 0 */ + if ((!(val1 & 0x07)) && + (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3) + && (val2 != 0x94)) + || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12) + && (val2 != 0x06)))) + goto ERROR1; + /* If Winbond SMBus, check address at 0x48. Asus doesn't support */ + if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || + ((val1 & 0x80) && (val2 == 0x5c)))) { + if (w83781d_read_value + (new_client, W83781D_REG_I2C_ADDR) != address) + goto ERROR1; + } + } + + /* We have either had a force parameter, or we have already detected the + Winbond. Put it now into bank 0 and Vendor ID High Byte */ + w83781d_write_value(new_client, W83781D_REG_BANK, + (w83781d_read_value(new_client, + W83781D_REG_BANK) & 0x78) | + 0x80); + + /* Determine the chip type. */ + if (kind <= 0) { + /* get vendor ID */ + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if ((val2 == 0x12) || (val2 == 0x06)) + vendid = asus; + else + goto ERROR1; + /* mask off lower bit, not reliable */ + val1 = + w83781d_read_value(new_client, W83781D_REG_WCHIPID) & 0xfe; + if (val1 == 0x10 && vendid == winbond) + kind = w83781d; + else if (val1 == 0x30 && vendid == winbond) + kind = w83782d; + else if (val1 == 0x40 && vendid == winbond && !is_isa) + kind = w83783s; + else if (val1 == 0x20 && vendid == winbond) + kind = w83627hf; + else if (val1 == 0x30 && vendid == asus && !is_isa) + kind = as99127f; + else if (val1 == 0x60 && vendid == winbond && is_isa) + kind = w83697hf; + else { + if (kind == 0) + dev_warn(&new_client->dev, + "Ignoring 'force' parameter for unknown chip at" + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == w83781d) { + type_name = "w83781d"; + client_name = "W83781D chip"; + } else if (kind == w83782d) { + type_name = "w83782d"; + client_name = "W83782D chip"; + } else if (kind == w83783s) { + type_name = "w83783s"; + client_name = "W83783S chip"; + } else if (kind == w83627hf) { + type_name = "w83627hf"; + client_name = "W83627HF chip"; + } else if (kind == as99127f) { + type_name = "as99127f"; + client_name = "AS99127F chip"; + } else if (kind == w83697hf) { + type_name = "w83697hf"; + client_name = "W83697HF chip"; + } else { + dev_err(&new_client->dev, "Internal error: unknown kind (%d)?!?", kind); + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, W83781D_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strncpy(new_client->dev.name, client_name, DEVICE_NAME_SIZE); + data->type = kind; + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* attach secondary i2c lm75-like clients */ + if (!is_isa) { + if (!(data->lm75 = kmalloc(2 * sizeof (struct i2c_client), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR4; + } + + memset(data->lm75, 0x00, 2 * sizeof (struct i2c_client)); + + id = i2c_adapter_id(adapter); + if (force_subclients[0] == id && force_subclients[1] == address) { + for (i = 2; i <= 3; i++) { + if (force_subclients[i] < 0x48 || + force_subclients[i] > 0x4f) { + dev_err(&new_client->dev, + "Invalid subclient address %d; must be 0x48-0x4f\n", + force_subclients[i]); + goto ERROR5; + } + } + w83781d_write_value(new_client, + W83781D_REG_I2C_SUBADDR, + (force_subclients[2] & 0x07) | + ((force_subclients[3] & 0x07) << + 4)); + data->lm75[0].addr = force_subclients[2]; + } else { + val1 = w83781d_read_value(new_client, + W83781D_REG_I2C_SUBADDR); + data->lm75[0].addr = 0x48 + (val1 & 0x07); + } + if (kind != w83783s) { + if (force_subclients[0] == id && + force_subclients[1] == address) { + data->lm75[1].addr = force_subclients[3]; + } else { + data->lm75[1].addr = + 0x48 + ((val1 >> 4) & 0x07); + } + if (data->lm75[0].addr == data->lm75[1].addr) { + dev_err(&new_client->dev, + "Duplicate addresses 0x%x for subclients.\n", + data->lm75[0].addr); + goto ERROR5; + } + } + if (kind == w83781d) + client_name = "W83781D subclient"; + else if (kind == w83782d) + client_name = "W83782D subclient"; + else if (kind == w83783s) + client_name = "W83783S subclient"; + else if (kind == w83627hf) + client_name = "W83627HF subclient"; + else if (kind == as99127f) + client_name = "AS99127F subclient"; + + for (i = 0; i <= 1; i++) { + i2c_set_clientdata(&data->lm75[i], NULL); /* store all data in w83781d */ + data->lm75[i].adapter = adapter; + data->lm75[i].driver = &w83781d_driver; + data->lm75[i].flags = 0; + strncpy(data->lm75[i].dev.name, client_name, + DEVICE_NAME_SIZE); + if (kind == w83783s) + break; + } + } else { + data->lm75 = NULL; + } + + device_create_file_in(new_client, 0); + if (kind != w83783s && kind != w83697hf) + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + device_create_file_in(new_client, 3); + device_create_file_in(new_client, 4); + device_create_file_in(new_client, 5); + device_create_file_in(new_client, 6); + if (kind != as99127f && kind != w83781d && kind != w83783s) { + device_create_file_in(new_client, 7); + device_create_file_in(new_client, 8); + } + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + if (kind != w83697hf) + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + if (kind != w83783s && kind != w83697hf) + device_create_file_temp(new_client, 3); + + if (kind != w83697hf) + device_create_file_vid(new_client); + + if (kind != w83697hf) + device_create_file_vrm(new_client); + + device_create_file_fan_div(new_client, 1); + device_create_file_fan_div(new_client, 2); + if (kind != w83697hf) + device_create_file_fan_div(new_client, 3); + + device_create_file_alarms(new_client); + + device_create_file_beep(new_client); + + if (kind != w83781d) { + device_create_file_pwm(new_client, 1); + device_create_file_pwm(new_client, 2); + device_create_file_pwmenable(new_client, 2); + } + if (kind == w83782d && !is_isa) { + device_create_file_pwm(new_client, 3); + device_create_file_pwm(new_client, 4); + } + + if (kind != as99127f && kind != w83781d) { + device_create_file_sensor(new_client, 1); + device_create_file_sensor(new_client, 2); + if (kind != w83783s && kind != w83697hf) + device_create_file_sensor(new_client, 3); + } +#ifdef W83781D_RT + if (kind == w83781d) { + device_create_file_rt(new_client, 1); + device_create_file_rt(new_client, 2); + device_create_file_rt(new_client, 3); + } +#endif + + /* Initialize the chip */ + w83781d_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR5: + if (!is_isa) { + i2c_detach_client(&data->lm75[0]); + if (data->type != w83783s) + i2c_detach_client(&data->lm75[1]); + kfree(data->lm75); + } + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, W83781D_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +static int +w83781d_detach_client(struct i2c_client *client) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + if (i2c_is_isa_client(client)) { + release_region(client->addr, W83781D_EXTENT); + } else { + i2c_detach_client(&data->lm75[0]); + if (data->type != w83783s) + i2c_detach_client(&data->lm75[1]); + kfree(data->lm75); + } + kfree(client); + + return 0; +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int +w83781d_read_value(struct i2c_client *client, u16 reg) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int res, word_sized, bank; + struct i2c_client *cl; + + down(&data->lock); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); + } else { + /* switch to subclient */ + cl = &data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = + swap_bytes(i2c_smbus_read_word_data(cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = + swap_bytes(i2c_smbus_read_word_data(cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = + swap_bytes(i2c_smbus_read_word_data(cl, 3)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + } + up(&data->lock); + return res; +} + +static int +w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int word_sized, bank; + struct i2c_client *cl; + + down(&data->lock); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); + } else { + /* switch to subclient */ + cl = &data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, + swap_bytes(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, + swap_bytes(value)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + } + up(&data->lock); + return 0; +} + +/* Called when we have found a new W83781D. It should set limits, etc. */ +static void +w83781d_init_client(struct i2c_client *client) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int vid = 0, i, p; + int type = data->type; + u8 tmp; + + if (init && type != as99127f) { /* this resets registers we don't have + documentation for on the as99127f */ + /* save these registers */ + i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + p = w83781d_read_value(client, W83781D_REG_PWMCLK12); + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); + /* Restore the registers and disable power-on abnormal beep. + This saves FAN 1/2/3 input/output values set by BIOS. */ + w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + w83781d_write_value(client, W83781D_REG_PWMCLK12, p); + /* Disable master beep-enable (reset turns it on). + Individual beep_mask should be reset to off but for some reason + disabling this bit helps some people not get beeped */ + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); + } + + if (type != w83697hf) { + vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f; + vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) << + 4; + data->vrm = DEFAULT_VRM; + vid = vid_from_reg(vid, data->vrm); + } + + if ((type != w83781d) && (type != as99127f)) { + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = W83781D_DEFAULT_BETA; + } else { + if (w83781d_read_value + (client, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83783s || type == w83697hf) && (i == 2)) + break; + } + } +#ifdef W83781D_RT +/* + Fill up the RT Tables. + We assume that they are 32 bytes long, in order for temp 1-3. + Data sheet documentation is sparse. + We also assume that it is only for the 781D although I suspect + that the others support it as well.... +*/ + + if (init && type == w83781d) { + u16 k = 0; +/* + Auto-indexing doesn't seem to work... + w83781d_write_value(client,W83781D_REG_RT_IDX,0); +*/ + for (i = 0; i < 3; i++) { + int j; + for (j = 0; j < 32; j++) { + w83781d_write_value(client, + W83781D_REG_RT_IDX, k++); + data->rt[i][j] = + w83781d_read_value(client, + W83781D_REG_RT_VAL); + } + } + } +#endif /* W83781D_RT */ + + if (init) { + w83781d_write_value(client, W83781D_REG_IN_MIN(0), + IN_TO_REG(W83781D_INIT_IN_MIN_0)); + w83781d_write_value(client, W83781D_REG_IN_MAX(0), + IN_TO_REG(W83781D_INIT_IN_MAX_0)); + if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_IN_MIN(1), + IN_TO_REG(W83781D_INIT_IN_MIN_1)); + w83781d_write_value(client, W83781D_REG_IN_MAX(1), + IN_TO_REG(W83781D_INIT_IN_MAX_1)); + } + + w83781d_write_value(client, W83781D_REG_IN_MIN(2), + IN_TO_REG(W83781D_INIT_IN_MIN_2)); + w83781d_write_value(client, W83781D_REG_IN_MAX(2), + IN_TO_REG(W83781D_INIT_IN_MAX_2)); + w83781d_write_value(client, W83781D_REG_IN_MIN(3), + IN_TO_REG(W83781D_INIT_IN_MIN_3)); + w83781d_write_value(client, W83781D_REG_IN_MAX(3), + IN_TO_REG(W83781D_INIT_IN_MAX_3)); + w83781d_write_value(client, W83781D_REG_IN_MIN(4), + IN_TO_REG(W83781D_INIT_IN_MIN_4)); + w83781d_write_value(client, W83781D_REG_IN_MAX(4), + IN_TO_REG(W83781D_INIT_IN_MAX_4)); + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83781D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83781D_INIT_IN_MAX_5)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83782D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83782D_INIT_IN_MAX_5)); + } + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83781D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83781D_INIT_IN_MAX_6)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83782D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83782D_INIT_IN_MAX_6)); + } + if ((type == w83782d) || (type == w83627hf) || + (type == w83697hf)) { + w83781d_write_value(client, W83781D_REG_IN_MIN(7), + IN_TO_REG(W83781D_INIT_IN_MIN_7)); + w83781d_write_value(client, W83781D_REG_IN_MAX(7), + IN_TO_REG(W83781D_INIT_IN_MAX_7)); + w83781d_write_value(client, W83781D_REG_IN_MIN(8), + IN_TO_REG(W83781D_INIT_IN_MIN_8)); + w83781d_write_value(client, W83781D_REG_IN_MAX(8), + IN_TO_REG(W83781D_INIT_IN_MAX_8)); + w83781d_write_value(client, W83781D_REG_VBAT, + (w83781d_read_value + (client, + W83781D_REG_VBAT) | 0x01)); + } + w83781d_write_value(client, W83781D_REG_FAN_MIN(1), + FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2)); + w83781d_write_value(client, W83781D_REG_FAN_MIN(2), + FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2)); + if (type != w83697hf) { + w83781d_write_value(client, W83781D_REG_FAN_MIN(3), + FAN_TO_REG(W83781D_INIT_FAN_MIN_3, + 2)); + } + + w83781d_write_value(client, W83781D_REG_TEMP_OVER(1), + TEMP_TO_REG(W83781D_INIT_TEMP_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST(1), + TEMP_TO_REG(W83781D_INIT_TEMP_HYST)); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP_OVER(2), + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST(2), + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } else { + w83781d_write_value(client, W83781D_REG_TEMP_OVER(2), + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST(2), + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } + w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP_OVER(3), + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST(3), + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } else if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_TEMP_OVER(3), + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST(3), + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } + if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG, + 0x00); + } + if (type != w83781d) { + /* enable comparator mode for temp2 and temp3 so + alarm indication will work correctly */ + w83781d_write_value(client, W83781D_REG_IRQ, 0x41); + for (i = 0; i < 3; i++) + data->pwmenable[i] = 1; + } + } + + /* Start monitoring */ + w83781d_write_value(client, W83781D_REG_CONFIG, + (w83781d_read_value(client, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); +} + +static void +w83781d_update_client(struct i2c_client *client) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after + (jiffies - data->last_updated, (unsigned long) (HZ + HZ / 2)) + || time_before(jiffies, data->last_updated) || !data->valid) { + pr_debug(KERN_DEBUG "Starting device update\n"); + + for (i = 0; i <= 8; i++) { + if ((data->type == w83783s || data->type == w83697hf) + && (i == 1)) + continue; /* 783S has no in1 */ + data->in[i] = + w83781d_read_value(client, W83781D_REG_IN(i)); + data->in_min[i] = + w83781d_read_value(client, W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83781d_read_value(client, W83781D_REG_IN_MAX(i)); + if ((data->type != w83782d) && (data->type != w83697hf) + && (data->type != w83627hf) && (i == 6)) + break; + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN(i)); + data->fan_min[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN_MIN(i)); + } + if (data->type != w83781d) { + for (i = 1; i <= 4; i++) { + data->pwm[i - 1] = + w83781d_read_value(client, + W83781D_REG_PWM(i)); + if (((data->type == w83783s) + || (data->type == w83627hf) + || (data->type == as99127f) + || (data->type == w83697hf) + || ((data->type == w83782d) + && i2c_is_isa_client(client))) + && i == 2) + break; + } + } + + data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1)); + data->temp_min = + w83781d_read_value(client, W83781D_REG_TEMP_OVER(1)); + data->temp_max = + w83781d_read_value(client, W83781D_REG_TEMP_HYST(1)); + data->temp_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP(2)); + data->temp_max_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP_OVER(2)); + data->temp_min_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP_HYST(2)); + if (data->type != w83783s && data->type != w83697hf) { + data->temp_add[1] = + w83781d_read_value(client, W83781D_REG_TEMP(3)); + data->temp_max_add[1] = + w83781d_read_value(client, + W83781D_REG_TEMP_OVER(3)); + data->temp_min_add[1] = + w83781d_read_value(client, + W83781D_REG_TEMP_HYST(3)); + } + i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + if (data->type != w83697hf) { + data->vid = i & 0x0f; + data->vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & + 0x01) + << 4; + } + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + if (data->type != w83697hf) { + data->fan_div[2] = (w83781d_read_value(client, + W83781D_REG_PIN) + >> 6) & 0x03; + } + if ((data->type != w83781d) && (data->type != as99127f)) { + i = w83781d_read_value(client, W83781D_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + if (data->type != w83697hf) + data->fan_div[2] |= (i >> 5) & 0x04; + } + data->alarms = + w83781d_read_value(client, + W83781D_REG_ALARM1) + + (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); + if ((data->type == w83782d) || (data->type == w83627hf)) { + data->alarms |= + w83781d_read_value(client, + W83781D_REG_ALARM3) << 16; + } + i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); + data->beep_enable = i >> 7; + data->beep_mask = ((i & 0x7f) << 8) + + w83781d_read_value(client, W83781D_REG_BEEP_INTS1); + if ((data->type != w83781d) && (data->type != as99127f)) { + data->beep_mask |= + w83781d_read_value(client, + W83781D_REG_BEEP_INTS3) << 16; + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +static int __init +sensors_w83781d_init(void) +{ + return i2c_add_driver(&w83781d_driver); +} + +static void __exit +sensors_w83781d_exit(void) +{ + i2c_del_driver(&w83781d_driver); +} + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " + "Philip Edelbrock <phil@netroedge.com>, " + "and Mark Studebaker <mdsxyz123@yahoo.com>"); +MODULE_DESCRIPTION("W83781D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83781d_init); +module_exit(sensors_w83781d_exit); diff -Nru a/include/linux/i2c-vid.h b/include/linux/i2c-vid.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/linux/i2c-vid.h Wed Apr 2 16:00:28 2003 @@ -0,0 +1,62 @@ +/* + vrm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> + With assistance from Trent Piepho <xyzzy@speakeasy.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This file contains common code for decoding VID pins. + This file is #included in various chip drivers in this directory. + As the user is unlikely to load more than one driver which + includes this code we don't worry about the wasted space. + Reference: VRM x.y DC-DC Converter Design Guidelines, + available at http://developer.intel.com +*/ + +/* + Legal val values 00 - 1F. + vrm is the Intel VRM document version. + Note: vrm version is scaled by 10 and the return value is scaled by 1000 + to avoid floating point in the kernel. +*/ + +#define DEFAULT_VRM 82 + +static inline int vid_from_reg(int val, int vrm) +{ + switch(vrm) { + + case 91: /* VRM 9.1 */ + case 90: /* VRM 9.0 */ + return(val == 0x1f ? 0 : + 1850 - val * 25); + + case 85: /* VRM 8.5 */ + return((val & 0x10 ? 25 : 0) + + ((val & 0x0f) > 0x04 ? 2050 : 1250) - + ((val & 0x0f) * 50)); + + case 84: /* VRM 8.4 */ + val &= 0x0f; + /* fall through */ + default: /* VRM 8.2 */ + return(val == 0x1f ? 0 : + val & 0x10 ? 5100 - (val) * 100 : + 2050 - (val) * 50); + } +} ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.977.29.13, 2003/04/02 14:18:09-08:00, greg@kroah.com i2c: clean up previous w83781d patch due to changes I made to i2c core and build. drivers/i2c/chips/Kconfig | 30 +++++++++++++++--------------- drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/w83781d.c | 8 +++----- include/linux/i2c-vid.h | 4 ++-- 4 files changed, 21 insertions(+), 22 deletions(-) diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:19 2003 +++ b/drivers/i2c/chips/Kconfig Wed Apr 2 16:00:19 2003 @@ -37,20 +37,6 @@ in the lm_sensors package, which you can download at http://www.lm-sensors.nu -config SENSORS_W83781D - tristate " Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" - depends on I2C && I2C_PROC - help - If you say yes here you get support for the Winbond W8378x series - of sensor chips: the W83781D, W83782D, W83783S and W83682HF, - and the similar Asus AS99127F. This - can also be built as a module which can be inserted and removed - while the kernel is running. - - You will also need the latest user-space utilties: you can find them - in the lm_sensors package, which you can download at - http://www.lm-sensors.nu - config SENSORS_VIA686A tristate " VIA686A" depends on I2C && EXPERIMENTAL @@ -64,9 +50,23 @@ in the lm_sensors package, which you can download at http://www.lm-sensors.nu +config SENSORS_W83781D + tristate " Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Winbond W8378x series + of sensor chips: the W83781D, W83782D, W83783S and W83682HF, + and the similar Asus AS99127F. This + can also be built as a module which can be inserted and removed + while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + config I2C_SENSOR tristate - depends on SENSORS_ADM1021 || SENSORS_LM75 || SENSORS_VIA686A + depends on SENSORS_ADM1021 || SENSORS_LM75 || SENSORS_VIA686A || SENSORS_W83781D default m endmenu diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile --- a/drivers/i2c/chips/Makefile Wed Apr 2 16:00:19 2003 +++ b/drivers/i2c/chips/Makefile Wed Apr 2 16:00:19 2003 @@ -5,3 +5,4 @@ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o +obj-$(CONFIG_SENSORS_W83781D) += w83781d.o diff -Nru a/drivers/i2c/chips/w83781d.c b/drivers/i2c/chips/w83781d.c --- a/drivers/i2c/chips/w83781d.c Wed Apr 2 16:00:19 2003 +++ b/drivers/i2c/chips/w83781d.c Wed Apr 2 16:00:19 2003 @@ -38,7 +38,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/i2c-proc.h> +#include <linux/i2c-sensor.h> #include <linux/i2c-vid.h> #include <asm/io.h> @@ -332,8 +332,7 @@ }; static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind); +static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83781d_detach_client(struct i2c_client *client); static int w83781d_read_value(struct i2c_client *client, u16 register); @@ -1031,8 +1030,7 @@ } static int -w83781d_detect(struct i2c_adapter *adapter, int address, - unsigned short flags, int kind) +w83781d_detect(struct i2c_adapter *adapter, int address, int kind) { int i = 0, val1 = 0, val2, id; struct i2c_client *new_client; diff -Nru a/include/linux/i2c-vid.h b/include/linux/i2c-vid.h --- a/include/linux/i2c-vid.h Wed Apr 2 16:00:19 2003 +++ b/include/linux/i2c-vid.h Wed Apr 2 16:00:19 2003 @@ -1,6 +1,6 @@ /* - vrm.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring + i2c-vid.h - Part of lm_sensors, Linux kernel modules for hardware + monitoring Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> With assistance from Trent Piepho <xyzzy@speakeasy.org> ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.1009, 2003/04/02 15:36:11-08:00, greg@kroah.com i2c: fix up broken drivers/i2c/busses build due to I2C_PROC now being gone. drivers/i2c/busses/Kconfig | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig --- a/drivers/i2c/busses/Kconfig Wed Apr 2 16:00:11 2003 +++ b/drivers/i2c/busses/Kconfig Wed Apr 2 16:00:11 2003 @@ -1,13 +1,12 @@ # # Sensor device configuration -# All depend on EXPERIMENTAL, I2C and I2C_PROC. # menu "I2C Hardware Sensors Mainboard support" config I2C_ALI15X3 tristate " ALI 15x3" - depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + depends on I2C && PCI && EXPERIMENTAL help If you say yes to this option, support will be included for the Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces. @@ -21,7 +20,7 @@ config I2C_AMD756 tristate " AMD 756/766" - depends on I2C && I2C_PROC + depends on I2C && EXPERIMENTAL help If you say yes to this option, support will be included for the AMD 756/766/768 mainboard I2C interfaces. @@ -38,7 +37,7 @@ config I2C_AMD8111 tristate " AMD 8111" - depends on I2C && I2C_PROC + depends on I2C && EXPERIMENTAL help If you say yes to this option, support will be included for the AMD 8111 mainboard I2C interfaces. @@ -55,7 +54,7 @@ config I2C_I801 tristate " Intel 801" - depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + depends on I2C && PCI && EXPERIMENTAL help If you say yes to this option, support will be included for the Intel 801 family of mainboard I2C interfaces. Specifically, the following @@ -78,7 +77,7 @@ config I2C_ISA tristate " ISA Bus support" - depends on I2C && I2C_PROC && ISA && EXPERIMENTAL + depends on I2C && ISA && EXPERIMENTAL help If you say yes to this option, support will be included for i2c interfaces that are on the ISA bus. @@ -96,7 +95,7 @@ config I2C_PIIX4 tristate " Intel PIIX4" - depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + depends on I2C && PCI && EXPERIMENTAL help If you say yes to this option, support will be included for the Intel PIIX4 family of mainboard I2C interfaces. Specifically, the following ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH @ 2003-04-03 0:15 ` Greg KH 0 siblings, 0 replies; 20+ messages in thread From: Greg KH @ 2003-04-03 0:15 UTC (permalink / raw) To: linux-kernel, sensors ChangeSet 1.1010, 2003/04/02 15:39:28-08:00, greg@kroah.com i2c: remove old proc documentation and add sysfs file documentation Documentation/i2c/proc-interface | 53 ----------- Documentation/i2c/sysfs-interface | 177 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 53 deletions(-) diff -Nru a/Documentation/i2c/proc-interface b/Documentation/i2c/proc-interface --- a/Documentation/i2c/proc-interface Wed Apr 2 16:00:02 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -i2c-core is the core i2c module (surprise!) which offers general routines on -which other modules build. You will find that all i2c-related modules depend -on this module, so it will (need to) be loaded whenever another i2c-related -module is loaded. Seen from the outside, the most interesting is the /proc -interface. Note that there is no corresponding sysctl interface! - -/proc/bus/i2c -============= - -Whenever i2c-core is loaded, you will find a file /proc/bus/i2c, which lists -all currently registered I2C adapters. Each line contains exactly one -I2C adapter. Each line has the following format: "i2c-%d\t%9s\t%-32s't%-32s\n", -which works out to four columns separated by tabs. Note that the file -will be empty, if no adapters are registered at all. - -Adapters are numbered from 0 upwards. The first column contains the number -of the adapter, for example "i2c-4" for adapter 4. The name listed is also -the name of the /proc file which lists all devices attached to it, and -of the /dev file which corresponds to this adapter. - -The second column documents what kind of adapter this is. Some adapters -understand the full I2C protocol, others only a subset called SMBus, -and yet others are some kind of pseudo-adapters that do not understand -i2c at all. Possible values in here are "i2c", "smbus", "i2c/smbus" -and "dummy". Because the SMBus protocol can be fully emulated by i2c -adapters, if you see "i2c" here, SMBus is supported too. There may -be some future adapters which support both specific SMBus commands and -general I2C, and they will display "i2c/smbus". - -The third and fourth column are respectively the algorithm and adapter -name of this adapter. Each adapter is associated with an algorithm, -and several adapters can share the same algorithm. The combination of -algorithm name and adapter name should be unique for an adapter, but -you can't really count on that yet. - - -/proc/bus/i2c-* -=============== - -Each registered adapter gets its own file in /proc/bus/, which lists -the devices registered to the adapter. Each line in such a file contains -one registered device. Each line has the following format: -"%02x\t%-32s\t%-32s\n", which works out to three columns separated by -tabs. Note that this file can be empty, if no devices are found on -the adapter. - -The first column contains the (hexadecimal) address of the client. As -only 7-bit addresses are supported at this moment, two digits are -enough. - -The second and third column are respectively the client name and the -driver name of this client. Each client is associated with a driver, -and several clients can share the same driver. diff -Nru a/Documentation/i2c/sysfs-interface b/Documentation/i2c/sysfs-interface --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/i2c/sysfs-interface Wed Apr 2 16:00:02 2003 @@ -0,0 +1,177 @@ +Naming and data format standards for sysfs files +------------------------------------------------ + +The libsensors library offers an interface to the raw sensors data +through the sysfs interface. See libsensors documentation and source for +more further information. + +An alternative method that some programs use is to access the sysfs +files directly. This document briefly describes the standards that the +drivers follow, so that an application program can scan for entries and +access this data in a simple and consistent way. + +If you are developing a userspace application please send us feedback on +this standard. + +Note that motherboards vary widely in the connections to sensor chips. +There is no standard that ensures, for example, that the second +temperature sensor is connected to the CPU, or that the second fan is on +the CPU. Therefore, programs must provide a facility for the user to +label or bind /proc entries for display. Sensor chips often have unused +inputs that should be ignored by user programs. + +Each chip gets its own directory in the sysfs /sys/devices tree. To +find all sensor chips, it is easier to follow the symlinks from +/sys/i2c/devices/ + +All sysfs values are fixed point numbers. To get the true value of some +of the values, you should divide by the specified value. + +There is only one value per file, unlike the older /proc specification. + +Alarms are direct indications read from the chips. The drivers do NOT +make comparisons of readings to thresholds. This allows violations +between readings to be caught and alarmed. The exact definition of an +alarm (for example, whether a threshold must be met or must be exceeded +to cause an alarm) is chip-dependent. + + +------------------------------------------------------------------------- + +sysfs entries are as follows: + + +Entry Function +----- -------- +alarms Alarm bitmask. + Read only. + Integer representation of one to four bytes. + A '1' bit means an alarm. + Chips should be programmed for 'comparator' mode so that + the alarm will 'come back' after you read the register + if it is still valid. + Generally a direct representation of a chip's internal + alarm registers; there is no standard for the position + of individual bits. + Bits are defined in kernel/include/sensors.h. + +beep_enable Beep/interrupt enable + 0 to disable. + 1 to enable. + Read/Write + +beep_mask Bitmask for beep. + Same format as 'alarms' with the same bit locations. + Read only. + +curr_max[1-n] Current max value + Fixed point XXXXX, divide by 1000 to get Amps. + Read/Write. + +curr_min[1-n] Current min or hysteresis value. + Preferably a hysteresis value, reported as a absolute + current, NOT a delta from the max value. + Fixed point XXXXX, divide by 1000 to get Amps. + Read/Write. + +curr_input[1-n] Current input value + Fixed point XXXXX, divide by 1000 to get Amps. + Read only. + +fan_min[1-3] Fan minimum value + Integer value indicating RPM + Read/Write. + +fan_input[1-3] Fan input value. + Integer value indicating RPM + Read only. + +fan_div[1-3] Fan divisor. + Integers in powers of two (1,2,4,8,16,32,64,128). + Some chips only support values 1,2,4,8. + See doc/fan-divisors for details. + +in_min[0-8] Voltage min value. + Fixed point value in form XXXX. Divide by 1000 to get + Volts. + Read/Write + +in_max[0-8] Voltage max value. + Fixed point value in form XXXX. Divide by 1000 to get + Volts. + Read/Write + +in_input[0-8] Voltage input value. + Fixed point value in form XXXX. Divide by 1000 to get + Volts. + Read only + Actual voltage depends on the scaling resistors on the + motherboard, as recommended in the chip datasheet. + This varies by chip and by motherboard. + Because of this variation, values are generally NOT scaled + by the chip driver, and must be done by the application. + However, some drivers (notably lm87 and via686a) + do scale, with various degrees of success. + These drivers will output the actual voltage. + First two values are read/write and third is read only. + Typical usage: + in_*0 CPU #1 voltage (not scaled) + in_*1 CPU #1 voltage (not scaled) + in_*2 3.3V nominal (not scaled) + in_*3 5.0V nominal (scaled) + in_*4 12.0V nominal (scaled) + in_*5 -12.0V nominal (scaled) + in_*6 -5.0V nominal (scaled) + in_*7 varies + in_*8 varies + +pwm[1-3] Pulse width modulation fan control. + Integer 0 - 255 + Read/Write + 255 is max or 100%. + Corresponds to the fans 1-3. + +pwm_enable[1-3] pwm enable + not always present even if pwm* is. + 0 to turn off + 1 to turn on + Read/Write + +sensor[1-3] Sensor type selection. + Integers 1,2,3, or thermistor Beta value (3435) + Read/Write. + +temp_max[1-3] Temperature max value. + Fixed point value in form XXXXX and should be divided by + 1000 to get degrees Celsius. + Read/Write value. + +temp_min[1-3] Temperature min or hysteresis value. + Fixed point value in form XXXXX and should be divided by + 1000 to get degrees Celsius. This is preferably a + hysteresis value, reported as a absolute temperature, + NOT a delta from the max value. + Read/Write value. + +temp_input[1-3] Temperature input value. + Read only value. + + If there are multiple temperature sensors, temp_*1 is + generally the sensor inside the chip itself, generally + reported as "motherboard temperature". temp_*2 and + temp_*3 are generally sensors external to the chip + itself, for example the thermal diode inside the CPU or + a thermistor nearby. + +vid CPU core voltage. + Read only. + Fixed point value in form XXXX corresponding to CPU core + voltage as told to the sensor chip. Divide by 1000 to + get Volts. Not always correct. + +vrm Voltage Regulator Module version number. + Read only. + Two digit number (XX), first is major version, second is + minor version. + Affects the way the driver calculates the core voltage from + the vid pins. See doc/vid for details. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH @ 2003-04-03 6:21 ` Albert Cranford 2003-04-03 6:33 ` Greg KH 1 sibling, 1 reply; 20+ messages in thread From: Albert Cranford @ 2003-04-03 6:21 UTC (permalink / raw) To: Greg KH; +Cc: linux-kernel, sensors I read the thread concerning the removal of proc.c & proc.h but hope that this does not go to Linus until the interface between i2c & sensors to application is somewhat defined. At the moment we have a sysctl API used by sensors, video and other i2c kernel applications that is working. Our desire to switch the sensors to sysfs interface should not break other applications. At least until we have a model/api to propose to these other drivers. In my personal home systems I use it87 driver and have been somewhat successful in switching to sysfs. A big blocking point is there is no application to read/set/monitor the driver, so it is basically unverified. I would hate to put other i2c applications in the same boat. Your thoughts? Albert Greg KH wrote: > ChangeSet 1.977.29.8, 2003/04/02 11:45:21-08:00, greg@kroah.com > > i2c: remove proc and sysctl code from i2c-proc as it is no longer used. > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 6:21 ` Albert Cranford @ 2003-04-03 6:33 ` Greg KH 2003-04-03 13:06 ` Albert Cranford 0 siblings, 1 reply; 20+ messages in thread From: Greg KH @ 2003-04-03 6:33 UTC (permalink / raw) To: Albert Cranford; +Cc: linux-kernel, sensors On Thu, Apr 03, 2003 at 01:21:13AM -0500, Albert Cranford wrote: > I read the thread concerning the removal of proc.c & proc.h > but hope that this does not go to Linus until the interface > between i2c & sensors to application is somewhat defined. > > At the moment we have a sysctl API used by sensors, video and > other i2c kernel applications that is working. The only in-kernel drivers that were using the sysctl/proc interface was the lm75 and adm1021 drivers. The video and other i2c kernel drivers do not use this interface at all. Those two drivers, and the two other chip drivers that I added to the kernel in this set of patches were converted over to the sysfs interface (well one of the new ones were, the other one will build and run, but doesn't export any sysfs files yet, that will change soon.) > Our desire to switch the sensors to sysfs interface should not > break other applications. At least until we have a model/api > to propose to these other drivers. Yes, any applications that used the sysctl interface to get data from those two driver will break. However we have to switch at some point in time, and the userspace library can't be worked on very well if the kernel can't support it yet :) So I'm choosing to update the kernel first, and will be working on the library in the coming weeks. As there is no real sensors support besides those two drivers in the main kernel, I didn't break much :) > In my personal home systems I use it87 driver and have been > somewhat successful in switching to sysfs. A big blocking > point is there is no application to read/set/monitor the > driver, so it is basically unverified. I tested the changes I did by using echo and cat, no library or other applications are needed just yet. thanks, greg k-h ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 6:33 ` Greg KH @ 2003-04-03 13:06 ` Albert Cranford 2003-04-03 16:37 ` Greg KH 0 siblings, 1 reply; 20+ messages in thread From: Albert Cranford @ 2003-04-03 13:06 UTC (permalink / raw) To: Greg KH; +Cc: linux-kernel, sensors Your right, nobody outside of sensors uses i2c-proc, but why not create a new i2c-sysfs.h in drivers/i2c locally and and adjust the couple of existing drivers. We all know the application library is not going to have access to include/linux/include/i2c-xxxx.h anyhow. Later we can completely remove linux/include/linux/i2c-proc.h which the existing application library relies upon. ALbert Greg KH wrote: > On Thu, Apr 03, 2003 at 01:21:13AM -0500, Albert Cranford wrote: > >>I read the thread concerning the removal of proc.c & proc.h >>but hope that this does not go to Linus until the interface >>between i2c & sensors to application is somewhat defined. >> >>At the moment we have a sysctl API used by sensors, video and >>other i2c kernel applications that is working. > > > The only in-kernel drivers that were using the sysctl/proc interface was > the lm75 and adm1021 drivers. The video and other i2c kernel drivers do > not use this interface at all. > > Those two drivers, and the two other chip drivers that I added to the > kernel in this set of patches were converted over to the sysfs interface > (well one of the new ones were, the other one will build and run, but > doesn't export any sysfs files yet, that will change soon.) > > >>Our desire to switch the sensors to sysfs interface should not >>break other applications. At least until we have a model/api >>to propose to these other drivers. > > > Yes, any applications that used the sysctl interface to get data from > those two driver will break. However we have to switch at some point in > time, and the userspace library can't be worked on very well if the > kernel can't support it yet :) > > So I'm choosing to update the kernel first, and will be working on the > library in the coming weeks. As there is no real sensors support > besides those two drivers in the main kernel, I didn't break much :) > > >>In my personal home systems I use it87 driver and have been >>somewhat successful in switching to sysfs. A big blocking >>point is there is no application to read/set/monitor the >>driver, so it is basically unverified. > > > I tested the changes I did by using echo and cat, no library or other > applications are needed just yet. > > thanks, > > greg k-h > > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] More i2c driver changes for 2.5.66 2003-04-03 13:06 ` Albert Cranford @ 2003-04-03 16:37 ` Greg KH 0 siblings, 0 replies; 20+ messages in thread From: Greg KH @ 2003-04-03 16:37 UTC (permalink / raw) To: Albert Cranford; +Cc: linux-kernel, sensors On Thu, Apr 03, 2003 at 08:06:06AM -0500, Albert Cranford wrote: > Your right, nobody outside of sensors uses i2c-proc, but > why not create a new i2c-sysfs.h in drivers/i2c locally > and and adjust the couple of existing drivers. What would i2c-sysfs.h be needed for? All of the needed sysfs prototype functions are already in device.h. > We all know the application library is not going to have > access to include/linux/include/i2c-xxxx.h anyhow. It never did :) > Later we can completely remove linux/include/linux/i2c-proc.h > which the existing application library relies upon. No userspace program should rely on kernel header files. And I didn't take away the functionality that i2c-proc.h provided with the list of devices supported and such. Just the unused function prototypes that dealt with the proc and sysctl interface. thanks, greg k-h ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2003-04-03 16:33 UTC | newest] Thread overview: 20+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2003-04-03 0:14 [BK PATCH] More i2c driver changes for 2.5.66 Greg KH 2003-04-03 0:15 ` [PATCH] " Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 0:15 ` Greg KH 2003-04-03 6:21 ` Albert Cranford 2003-04-03 6:33 ` Greg KH 2003-04-03 13:06 ` Albert Cranford 2003-04-03 16:37 ` Greg KH
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox