Index: /lm-sensors/branches/scanner-opt-branch/BACKGROUND
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/BACKGROUND	(revision 568)
+++ /lm-sensors/branches/scanner-opt-branch/BACKGROUND	(revision 568)
@@ -0,0 +1,70 @@
+This package consists of many parts. This document offers a small tour
+around the most important pieces. Not all aspects are discussed 
+exhaustively, but it should be enough to give you the feeling you
+understand what you are seeing happen.
+
+When you want to communicate with a piece of hardware, you need a kernel
+driver (well, that is not quite true, but it is in most cases the only
+way to do it safely). In the past, this meant you had to patch the kernel
+and recompile it. These days, you can use kernel modules. We support both
+approaches.
+
+We have chooses for a very modular (no pun intended) setup. There are a
+few general-purpose base kernel modules, which you always need. In 
+addition, there is one kernel module for each piece of hardware - whether
+this is an I2C bus adapter, an SMBus adapter or a sensors chip.
+
+The kernel modules communicate their information through both the /proc
+and sysctl interfaces. To keep things uncomplicated, the sensor chips always 
+advert their measurements 'as is'. This means that the values they
+report are not immediately relevant to you - they must first be scaled
+and translated to correspond to the real world.
+
+It is also possible to communicate directly with chips on an I2C bus
+or SMBus. This is done through /dev files. This is useful if you 
+quickly want to test how a certain chip behaves, without having to
+write a kernel driver. It is also dangerous; several applications could
+access the same chip at the same time.
+
+Note that all other parts of this package function in so-called user-space.
+This is important, because bugs in kernel-space might crash your
+computer or do other bad things. And kernel memory can not be swapped out!
+
+Applications could (and can) directly read the sensor values through the 
+/proc or the sysctl interfaces. This is harder then it sounds; because
+no two chips are the same, the information they communicate may also
+be very unlike. This would mean that every application would have to know
+about every type of chip. But there is a better solution.
+
+libsensors is a (shared or static) library of access functions. It
+knows about every type of chip supported by the kernel modules (or it
+should, if it is up-to-date). It offers a simple-to-use interface for
+applications to access the sensor chip readings, to set new limits,
+and all other commonly needed things. From the application's point of
+view, there is no need to know very much about a specific sensors chip. 
+Having some inside information can still be useful, but it is possible to
+write a generic fall-back function that takes care of newer, unknown
+chips, and to display all really important information.
+
+libsensors takes care of one other thing. The kernel modules report 
+so-called 'as is' values. They have to be scaled or translated to be 
+relevant in the real world. libsensors reads a configuration file 
+(usually /etc/sensors.conf) which specifies how this translation should
+be done (with some other things). Again, the application does not have
+to know about it. And because the configuration file is reread each time
+a new application is started, you can change configuration values without
+having to recompile anything.
+
+This package does not contain a nice graphical monitor. Look at the file
+doc/useful_addresses for pointers to such programs. It does contain an
+example console program that reports all current sensors values. This
+program is called 'sensors'. You can use it as a reference implementation
+for more intricate programs.
+
+There are many, many kernel modules in this package, and there are lots
+of different sensor chips supported. Sometimes, it can be hard to 
+determine what chips and adapters you have, and which modules correspond
+to them. Fortunately, there is a user-space application 'sensors-detect'
+that should tell you exactly what is available, and what you need to
+do. This perl script uses the /dev interface, and you may use it as an
+example how you can do this.
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-dev.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-dev.h	(revision 3177)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-dev.h	(revision 3177)
@@ -0,0 +1,325 @@
+/*
+    i2c-dev.h - i2c-bus driver, char device interface
+
+    Copyright (C) 1995-97 Simon G. Vogl
+    Copyright (C) 1998-99 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.
+*/
+
+/* $Id$ */
+
+#ifndef LIB_I2CDEV_H
+#define LIB_I2CDEV_H
+
+#include <linux/types.h>
+#include <sys/ioctl.h>
+
+
+/* -- i2c.h -- */
+
+
+/*
+ * I2C Message - used for pure i2c transaction, also from /dev interface
+ */
+struct i2c_msg {
+	__u16 addr;	/* slave address			*/
+	unsigned short flags;		
+#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
+#define I2C_M_RD	0x01
+#define I2C_M_NOSTART	0x4000
+#define I2C_M_REV_DIR_ADDR	0x2000
+#define I2C_M_IGNORE_NAK	0x1000
+#define I2C_M_NO_RD_ACK		0x0800
+	short len;		/* msg length				*/
+	char *buf;		/* pointer to msg data			*/
+};
+
+/* To determine what functionality is present */
+
+#define I2C_FUNC_I2C			0x00000001
+#define I2C_FUNC_10BIT_ADDR		0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_HWPEC_CALC	0x00000008 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK		0x00010000 
+#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000 
+#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000 
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000 
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000 
+#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000 
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000 
+#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000 
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000 
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2	 0x10000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
+
+#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
+                             I2C_FUNC_SMBUS_WRITE_BYTE)
+#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
+#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
+                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
+#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
+#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
+#define I2C_FUNC_SMBUS_I2C_BLOCK_2 (I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
+                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2)
+
+/* 
+ * Data for SMBus Messages 
+ */
+#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */	
+#define I2C_SMBUS_I2C_BLOCK_MAX	32	/* Not specified but we use same structure */
+union i2c_smbus_data {
+	__u8 byte;
+	__u16 word;
+	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
+	                                            /* and one more for PEC */
+};
+
+/* smbus_access read or write markers */
+#define I2C_SMBUS_READ	1
+#define I2C_SMBUS_WRITE	0
+
+/* SMBus transaction types (size parameter in the above functions) 
+   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
+#define I2C_SMBUS_QUICK		    0
+#define I2C_SMBUS_BYTE		    1
+#define I2C_SMBUS_BYTE_DATA	    2 
+#define I2C_SMBUS_WORD_DATA	    3
+#define I2C_SMBUS_PROC_CALL	    4
+#define I2C_SMBUS_BLOCK_DATA	    5
+#define I2C_SMBUS_I2C_BLOCK_DATA    6
+#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */
+
+
+/* ----- commands for the ioctl like i2c_command call:
+ * note that additional calls are defined in the algorithm and hw 
+ *	dependent layers - these can be listed here, or see the 
+ *	corresponding header files.
+ */
+				/* -> bit-adapter specific ioctls	*/
+#define I2C_RETRIES	0x0701	/* number of times a device address      */
+				/* should be polled when not            */
+                                /* acknowledging 			*/
+#define I2C_TIMEOUT	0x0702	/* set timeout - call with int 		*/
+
+
+/* this is for i2c-dev.c	*/
+#define I2C_SLAVE	0x0703	/* Change slave address			*/
+				/* Attn.: Slave address is 7 or 10 bits */
+#define I2C_SLAVE_FORCE	0x0706	/* Change slave address			*/
+				/* Attn.: Slave address is 7 or 10 bits */
+				/* This changes the address, even if it */
+				/* is already taken!			*/
+#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit	*/
+
+#define I2C_FUNCS	0x0705	/* Get the adapter functionality */
+#define I2C_RDWR	0x0707	/* Combined R/W transfer (one stop only)*/
+#define I2C_PEC		0x0708	/* != 0 for SMBus PEC                   */
+
+#define I2C_SMBUS	0x0720	/* SMBus-level access */
+
+/* -- i2c.h -- */
+
+
+/* Note: 10-bit addresses are NOT supported! */
+
+/* This is the structure as used in the I2C_SMBUS ioctl call */
+struct i2c_smbus_ioctl_data {
+	char read_write;
+	__u8 command;
+	int size;
+	union i2c_smbus_data *data;
+};
+
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
+	int nmsgs;		/* number of i2c_msgs */
+};
+
+
+static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 
+                                     int size, union i2c_smbus_data *data)
+{
+	struct i2c_smbus_ioctl_data args;
+
+	args.read_write = read_write;
+	args.command = command;
+	args.size = size;
+	args.data = data;
+	return ioctl(file,I2C_SMBUS,&args);
+}
+
+
+static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
+{
+	return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
+}
+	
+static inline __s32 i2c_smbus_read_byte(int file)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
+		return -1;
+	else
+		return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
+{
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
+	                        I2C_SMBUS_BYTE,NULL);
+}
+
+static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_BYTE_DATA,&data))
+		return -1;
+	else
+		return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 
+                                              __u8 value)
+{
+	union i2c_smbus_data data;
+	data.byte = value;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_BYTE_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_WORD_DATA,&data))
+		return -1;
+	else
+		return 0x0FFFF & data.word;
+}
+
+static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 
+                                              __u16 value)
+{
+	union i2c_smbus_data data;
+	data.word = value;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_WORD_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
+{
+	union i2c_smbus_data data;
+	data.word = value;
+	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                     I2C_SMBUS_PROC_CALL,&data))
+		return -1;
+	else
+		return 0x0FFFF & data.word;
+}
+
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 
+                                              __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_BLOCK_DATA,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 
+                                               __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_BLOCK_DATA, &data);
+}
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
+                                                  __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                      I2C_SMBUS_I2C_BLOCK_DATA,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
+                                               __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_I2C_BLOCK_DATA, &data);
+}
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
+                                                 __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                     I2C_SMBUS_BLOCK_PROC_CALL,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+
+#endif /* LIB_I2CDEV_H */
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_vid.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_vid.h	(revision 3190)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_vid.h	(revision 3190)
@@ -0,0 +1,93 @@
+/*
+    sensors_vid.h - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2002-2004  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 sensor chip drivers.
+    As the user is unlikely to load more than one driver which
+    includes this code we don't worry about the wasted space.
+    References: VRM x.y DC-DC Converter Design Guidelines,
+                VRD 10.0 Design Guide,
+                available at http://developer.intel.com
+*/
+
+/*
+    AMD Opteron processors don't follow the Intel VRM spec.
+    I'm going to "make up" 2.4 as the VRM spec for the Opterons.
+    No good reason just a mnemonic for the 24x Opteron processor
+    series
+
+    Opteron VID encoding is:
+
+       00000  =  1.550 V
+       00001  =  1.525 V
+        . . . .
+       11110  =  0.800 V
+       11111  =  0.000 V (off)
+ */
+
+/*
+    Legal val values 00 - 1F except for VRD 10.0, 0x00-0x3f.
+    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.
+*/
+
+static inline int vid_from_reg(int val, int vrm)
+{
+	int vid;
+
+	switch(vrm) {
+
+	case 100:		/* VRD 10.0 */
+		if((val & 0x1f) == 0x1f)
+			return 0;
+		if((val & 0x1f) <= 0x09 || val == 0x0a)
+			vid = 10875 - (val & 0x1f) * 250;
+		else
+			vid = 18625 - (val & 0x1f) * 250;
+		if(val & 0x20)
+			vid -= 125;
+		vid /= 10;	/* only return 3 dec. places for now */
+		return vid;
+
+	case 24:		/* Opteron processor */
+		return(val == 0x1f ? 0 : 1550 - val * 25);
+
+	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);
+	}
+}
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_compat.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_compat.h	(revision 2801)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors_compat.h	(revision 2801)
@@ -0,0 +1,51 @@
+/*
+ * Stolen from kernel 2.5.69
+ * device.h - generic, centralized driver model
+ * To make it easier to backport from 2.5
+ *
+ * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
+ *
+ */
+
+#ifndef _SENSORS_COMPAT_H_
+#define _SENSORS_COMPAT_H_
+
+#include <linux/config.h>
+
+/* debugging and troubleshooting/diagnostic helpers. */
+#define dev_printk(level, dev, format, arg...)	\
+	printk(level "%s: " format , (dev)->name , ## arg)
+
+#ifdef DEBUG
+#define dev_dbg(dev, format, arg...)		\
+	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#else
+#define dev_dbg(dev, format, arg...) do {} while (0)
+#endif
+
+#define dev_err(dev, format, arg...)		\
+	dev_printk(KERN_ERR , dev , format , ## arg)
+#define dev_info(dev, format, arg...)		\
+	dev_printk(KERN_INFO , dev , format , ## arg)
+#define dev_warn(dev, format, arg...)		\
+	dev_printk(KERN_WARNING , dev , format , ## arg)
+
+
+/* The part below, taken from linux/init.h, is required for compatibility with
+   kernels 2.4.16 and older, which don't know about __devexit_p. */
+
+/* Functions marked as __devexit may be discarded at kernel link time, depending
+   on config options.  Newer versions of binutils detect references from
+   retained sections to discarded sections and flag an error.  Pointers to
+   __devexit functions must use __devexit_p(function_name), the wrapper will
+   insert either the function_name or NULL, depending on the config options.
+ */
+#ifndef __devexit_p
+#if defined(MODULE) || defined(CONFIG_HOTPLUG)
+#define __devexit_p(x) x
+#else
+#define __devexit_p(x) NULL
+#endif
+#endif /* __devexit_p */
+
+#endif /* _SENSORS_COMPAT_H_ */
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/ds1307.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/ds1307.h	(revision 1433)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/ds1307.h	(revision 1433)
@@ -0,0 +1,59 @@
+#ifndef DS1307_H
+#define DS1307_H
+
+/*
+ * linux/include/linux/ds1307.h
+ *
+ * Author: Abraham van der Merwe <abraham@2d3d.co.za>
+ *
+ * Linux support for the Dallas Semiconductor DS1307 Serial Real-Time
+ * Clock.
+ *
+ * Based on code from the lm-sensors project which is available
+ * at http://www.lm-sensors.nu/ and Russell King's PCF8583 Real-Time
+ * Clock driver (linux/drivers/acorn/char/pcf8583.c).
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+/* practically the same as struct rtc_time, but without tm_yday and tm_isdst */
+struct ds1307_date {
+	int tm_sec;
+	int tm_min;
+	int tm_hour;
+	int tm_mday;
+	int tm_mon;
+	int tm_year;
+	int tm_wday;
+};
+
+struct ds1307_memory {
+	u8 offset;		/* 0 - 55 */
+	u8 buf[56];		/* data */
+	u8 length;		/* offset + length <= 50 */
+};
+
+/* size of the rtc non-volatile ram */
+#define DS1307_SIZE	56
+
+/* the following frequencies are supported */
+#define DS1307_FREQ_1HZ		1
+#define DS1307_FREQ_4KHZ	4096
+#define DS1307_FREQ_8KHZ	8192
+#define DS1307_FREQ_32KHZ	32768
+
+#define DS1307_GET_DATE	_IOR ('d',0,struct ds1307_date *)
+#define DS1307_SET_DATE	_IOW ('d',1,struct ds1307_date *)
+#define DS1307_IRQ_ON	_IO  ('d',2)
+#define DS1307_IRQ_OFF	_IO  ('d',3)
+#define DS1307_GET_FREQ	_IOR ('d',4,u16 *)
+#define DS1307_SET_FREQ	_IOW ('d',5,u16 *)
+#define DS1307_READ		_IOR ('d',6,struct ds1307_memory *)
+#define DS1307_WRITE	_IOW ('d',7,struct ds1307_memory *)
+#define DS1307_ENABLE	_IO  ('d',8)
+
+#endif	/* #ifdef DS1307_H */
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-virtual.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-virtual.h	(revision 2765)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/i2c-virtual.h	(revision 2765)
@@ -0,0 +1,71 @@
+/*
+ * i2c-virtual.h - Header file for the 'virtual i2c' adapter driver.
+ *
+ * Copyright (c) 2004  Google, Inc.
+ *
+ * Based on:
+ *    i2c-virtual.h from Brian Kuschak <bkuschak@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.
+ */
+#ifndef __I2C_VIRTUAL_H
+#define __I2C_VIRTUAL_H
+
+#include <linux/i2c.h>
+
+struct i2c_mux_ctrl {
+	struct i2c_client *client;	/* The mux chip/device */
+
+	unsigned long	addr;		
+	unsigned long	value;		/* Channel # */
+
+	/* fn which enables the mux */
+	int (*select)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
+
+	/* fn which disables the mux */
+	int (*deselect)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
+};
+
+/* This has to be exposed, since the code which assigns the callbacks must
+   use it.
+ */
+struct i2c_virt_priv {
+	struct i2c_adapter  *parent_adap;	/* pointer to parent adapter */
+	struct i2c_mux_ctrl  mux;	/* MUX settings for this adapter */
+};
+
+
+static inline struct i2c_adapter *i2c_virt_parent(struct i2c_adapter *adap)
+{
+	if (adap->algo && (adap->algo->id == I2C_ALGO_VIRT)) {
+                return ((struct i2c_virt_priv *)(adap->data))->parent_adap;
+        }
+        return NULL;
+}
+
+/*
+ * Called to create a 'virtual' i2c bus which represents a multiplexed bus
+ * segment.  The client and mux_val are passed to the select and deselect
+ * callback functions to perform hardware-specific mux control.
+ */
+struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap, 
+                                           struct i2c_client *client,
+                                           unsigned long mux_val, 
+                                           void *select_cb,
+                                           void *deselect_cb);
+int i2c_virt_remove_adapter(struct i2c_adapter *adap);
+
+
+#endif /* __I2C_VIRTUAL_H */
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/Module.mk
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/Module.mk	(revision 2604)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/Module.mk	(revision 2604)
@@ -0,0 +1,55 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := kernel/include
+KERNELINCLUDEDIR := $(MODULE_DIR)
+KERNELCHIPSDIR := kernel/chips
+
+KERNELINCLUDEFILES := $(MODULE_DIR)/i2c-dev.h $(MODULE_DIR)/sensors.h
+
+$(KERNELINCLUDEDIR)/sensors.h: $(KERNELINCLUDEDIR)/sensors.h.template
+	cat $@.template > $@
+	$(AWK) '/SENSORS SYSCTL START/,/SENSORS SYSCTL END/' $(KERNELCHIPSDIR)/*.c >> $@
+	echo '#endif' >> $@
+
+$(KERNELINCLUDEDIR)/sensors.hd:
+	( $(GREP) 'SENSORS SYSCTL START' /dev/null $(KERNELCHIPSDIR)/*.c | \
+	  $(SED) -e 's/:.*//' -e 's#^#$(KERNELINCLUDEDIR)/sensors.h: #' ) > $@
+
+# Get dependencies of sensors.h
+INCLUDEFILES += $(MODULE_DIR)/sensors.hd
+
+REMOVEKERNELINC := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(SYSINCLUDEDIR)/%,$(KERNELINCLUDEFILES))
+
+install-all-kernel-include:
+	if [ -n "$(KERNELINCLUDEFILES)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(SYSINCLUDEDIR) ; \
+	  $(INSTALL) -m 644 $(KERNELINCLUDEFILES) $(DESTDIR)$(SYSINCLUDEDIR) ; \
+	fi
+
+user_install :: install-all-kernel-include
+
+user_uninstall::
+	$(RM) $(REMOVEKERNELINC)
+
+clean-all-kernel-include:
+	$(RM) $(KERNELINCLUDEDIR)/*.h.install $(KERNELINCLUDEDIR)/sensors.h $(KERNELINCLUDEDIR)/sensors.hd
+
+clean :: clean-all-kernel-include
Index: /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors.h.template
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors.h.template	(revision 2699)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/include/sensors.h.template	(revision 2699)
@@ -0,0 +1,55 @@
+/*
+    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.
+
+    --- THIS FILE IS AUTOGENERATED DO NOT EDIT --
+*/
+
+#ifndef LIB_SENSORS_H
+#define LIB_SENSORS_H
+
+/* This file is intended to be included from userland utilities only.
+ * 
+ * Individual drivers define their own SYSCTL and ALARM values in
+ * the driver itself surrounded by the following 'trigger' lines:
+ *
+ *   -- SENSORS SYSCTL START --
+ *   -- SENSORS SYSCTL END --
+ */
+
+
+/* From linux/i2c-proc.h */
+
+/* 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 */
+
+/* The maximum length of the prefix */
+#define SENSORS_PREFIX_MAX 20
+
+#define SENSORS_CHIPS 1
+struct i2c_chips_data {
+	int sysctl_id;
+	char name[SENSORS_PREFIX_MAX + 13];
+};
+
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1535.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1535.c	(revision 3291)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1535.c	(revision 3291)
@@ -0,0 +1,529 @@
+/*
+    i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware
+                    monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>, 
+                        Philip Edelbrock <phil@netroedge.com>, 
+                        Mark D. Studebaker <mdsxyz123@yahoo.com>,
+                        Dan Eaton <dan.eaton@rocketlogix.com> and 
+                        Stephen Rousset<stephen.rousset@rocketlogix.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 is the driver for the SMB Host controller on
+    Acer Labs Inc. (ALI) M1535 South Bridge.
+
+    The M1535 is a South bridge for portable systems.
+    It is very similar to the M15x3 South bridges also produced
+    by Acer Labs Inc.  Some of the registers within the part
+    have moved and some have been redefined slightly. Additionally,
+    the sequencing of the SMBus transactions has been modified
+    to be more consistent with the sequencing recommended by
+    the manufacturer and observed through testing.  These
+    changes are reflected in this driver and can be identified
+    by comparing this driver to the i2c-ali15x3 driver.
+    For an overview of these chips see http://www.acerlabs.com
+
+    The SMB controller is part of the 7101 device, which is an
+    ACPI-compliant Power Management Unit (PMU).
+
+    The whole 7101 device has to be enabled for the SMB to work.
+    You can't just enable the SMB alone.
+    The SMB and the ACPI have separate I/O spaces.
+    We make sure that the SMB is enabled. We leave the ACPI alone.
+
+    This driver controls the SMB Host only.
+
+    This driver does not use interrupts.
+*/
+
+
+/* Note: we assume there can only be one ALI1535, with one SMBus interface */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+
+/* ALI1535 SMBus address offsets */
+#define SMBHSTSTS (0 + ali1535_smba)
+#define SMBHSTTYP (1 + ali1535_smba)
+#define SMBHSTPORT (2 + ali1535_smba)
+#define SMBHSTCMD (7 + ali1535_smba)
+#define SMBHSTADD (3 + ali1535_smba)
+#define SMBHSTDAT0 (4 + ali1535_smba)
+#define SMBHSTDAT1 (5 + ali1535_smba)
+#define SMBBLKDAT (6 + ali1535_smba)
+
+/* PCI Address Constants */
+#define SMBCOM    0x004
+#define SMBREV    0x008
+#define SMBCFG    0x0D1
+#define SMBBA     0x0E2
+#define SMBHSTCFG 0x0F0
+#define SMBCLK    0x0F2
+
+/* Other settings */
+#define MAX_TIMEOUT 500		/* times 1/100 sec */
+#define ALI1535_SMB_IOSIZE 32
+
+/* 
+*/
+#define ALI1535_SMB_DEFAULTBASE 0x8040
+
+/* ALI1535 address lock bits */
+#define ALI1535_LOCK	0x06 < dwe >
+
+/* ALI1535 command constants */
+#define ALI1535_QUICK      0x00
+#define ALI1535_BYTE       0x10
+#define ALI1535_BYTE_DATA  0x20
+#define ALI1535_WORD_DATA  0x30
+#define ALI1535_BLOCK_DATA 0x40
+#define ALI1535_I2C_READ   0x60
+
+#define	ALI1535_DEV10B_EN	0x80	/* Enable 10-bit addressing in */
+                                        /*  I2C read                   */
+#define	ALI1535_T_OUT		0x08	/* Time-out Command (write)    */
+#define	ALI1535_A_HIGH_BIT9	0x08	/* Bit 9 of 10-bit address in  */
+                                        /* Alert-Response-Address      */
+                                        /* (read)                      */
+#define	ALI1535_KILL		0x04	/* Kill Command (write)        */
+#define	ALI1535_A_HIGH_BIT8	0x04	/* Bit 8 of 10-bit address in  */
+                                        /*  Alert-Response-Address     */
+                                        /*  (read)                     */
+
+#define	ALI1535_D_HI_MASK	0x03	/* Mask for isolating bits 9-8 */
+                                        /*  of 10-bit address in I2C   */ 
+                                        /*  Read Command               */
+
+/* ALI1535 status register bits */
+#define ALI1535_STS_IDLE	0x04
+#define ALI1535_STS_BUSY	0x08	/* host busy */
+#define ALI1535_STS_DONE	0x10	/* transaction complete */
+#define ALI1535_STS_DEV		0x20	/* device error */
+#define ALI1535_STS_BUSERR	0x40	/* bus error    */
+#define ALI1535_STS_FAIL	0x80    /* failed bus transaction */
+#define ALI1535_STS_ERR		0xE0	/* all the bad error bits */
+
+#define ALI1535_BLOCK_CLR	0x04	/* reset block data index */
+
+/* ALI1535 device address register bits */
+#define	ALI1535_RD_ADDR		0x01	/* Read/Write Bit in Device    */
+                                        /*  Address field              */
+                                        /*  -> Write = 0               */
+                                        /*  -> Read  = 1               */
+#define	ALI1535_SMBIO_EN	0x04	/* SMB I/O Space enable        */
+
+
+static struct pci_driver ali1535_driver;
+static unsigned short ali1535_smba = 0;
+
+
+/* Detect whether a ALI1535 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+int ali1535_setup(struct pci_dev *ALI1535_dev)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+/* Check the following things:
+	- SMB I/O address is initialized
+	- Device is enabled
+	- We can use the addresses
+*/
+
+/* Determine the address of the SMBus area */
+	pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba);
+	ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1));
+	if (ali1535_smba == 0) {
+		printk
+		    ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n");
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+	if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) {
+		printk
+		    ("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n",
+		     ali1535_smba);
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+	/* check if whole device is enabled */
+	pci_read_config_byte(ALI1535_dev, SMBCFG, &temp);
+	if ((temp & ALI1535_SMBIO_EN) == 0) {
+		printk
+		    ("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Is SMB Host controller enabled? */
+	pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp);
+	if ((temp & 1) == 0) {
+		printk
+		    ("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* set SMB clock to 74KHz as recommended in data sheet */
+	pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(ali1535_smba, ALI1535_SMB_IOSIZE, ali1535_driver.name);
+
+#ifdef DEBUG
+/*
+  The interrupt routing for SMB is set up in register 0x77 in the
+  1533 ISA Bridge device, NOT in the 7101 device.
+  Don't bother with finding the 1533 device and reading the register.
+  if ((....... & 0x0F) == 1)
+     printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n");
+*/
+	pci_read_config_byte(ALI1535_dev, SMBREV, &temp);
+	printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+#define MAX_TIMEOUT_HEROS		100
+#define MAX_TIMEOUT_BLOCK_HEROS	500
+#define MAX_TRY_HEROS			3
+
+/* believe it or not this is the delay technique recommended by ALI */
+static void ali1535_delay_loop(void)
+{
+	int i;
+
+	for(i=0;i<30;i++)
+		outb_p(0,0xEB);
+}
+
+static int ali1535_wait_for_status(int count,int status)
+{
+	int i;
+	int dat;
+
+	for (i = 0; i < count; i++) {
+		ali1535_delay_loop();
+		dat = inb_p(SMBHSTSTS);
+		if (dat == status)
+			break;
+	}
+
+	return i == count ? 1 : 0;
+}
+
+/* Return -1 on error. */
+s32 ali1535_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write, u8 command,
+		   int size, union i2c_smbus_data * data)
+{
+	int i, len;
+	s32 result = 0;
+	int timeout = 0;
+	int oldsize = size;
+
+repeat:
+	if(timeout++ > MAX_TRY_HEROS) {
+		result = -1;
+		goto EXIT;
+	}
+
+	/* clear status */
+	outb_p(0xFF, SMBHSTSTS);
+
+	if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_IDLE))
+		goto repeat;
+
+	outb_p(ALI1535_KILL,SMBHSTTYP);
+
+	if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_FAIL))
+		goto repeat;
+
+	/* clear status */
+	outb_p(0xFF, SMBHSTSTS);
+
+	if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_IDLE))
+		goto repeat;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
+		ali1535_delay_loop();
+		size = ALI1535_QUICK;
+		outb_p(size, SMBHSTTYP);	/* output command */
+		ali1535_delay_loop();
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
+		ali1535_delay_loop();
+		size = ALI1535_BYTE;
+		outb_p(size, SMBHSTTYP);	/* output command */
+		ali1535_delay_loop();
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(command, SMBHSTCMD);
+			ali1535_delay_loop();
+		}
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
+		ali1535_delay_loop();
+		size = ALI1535_BYTE_DATA;
+		outb_p(size, SMBHSTTYP);	/* output command */
+		ali1535_delay_loop();
+		outb_p(command, SMBHSTCMD);
+		ali1535_delay_loop();
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->byte, SMBHSTDAT0);
+			ali1535_delay_loop();
+		}
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
+		ali1535_delay_loop();
+		size = ALI1535_WORD_DATA;
+		outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		ali1535_delay_loop();
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			ali1535_delay_loop();
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+			ali1535_delay_loop();
+		}
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
+		ali1535_delay_loop();
+		size = ALI1535_BLOCK_DATA;
+		outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		ali1535_delay_loop();
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0) {
+				len = 0;
+				data->block[0] = len;
+			}
+			if (len > 32) {
+				len = 32;
+				data->block[0] = len;
+			}
+			outb_p(len, SMBHSTDAT0);
+			ali1535_delay_loop();
+			{
+				int val;
+
+				val = inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR;
+				ali1535_delay_loop();
+				outb_p(val, SMBHSTTYP);	/* Reset SMBBLKDAT */
+				ali1535_delay_loop();
+			}
+			for (i = 1; i <= len; i++) {
+				outb_p(data->block[i], SMBBLKDAT);
+				ali1535_delay_loop();
+			}
+		}
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-ali1535.o: Unsupported transaction %d\n", size);
+		result = -1;
+		goto EXIT;
+	}
+
+	/* start the transaction */
+	outb_p(0xFF, SMBHSTPORT);
+	ali1535_delay_loop();
+
+	if (ali1535_wait_for_status(size == ALI1535_BLOCK_DATA ?
+                                            MAX_TIMEOUT_BLOCK_HEROS :
+                                            MAX_TIMEOUT_HEROS ,
+                                       ALI1535_STS_IDLE | ALI1535_STS_DONE)) {
+		size = oldsize;
+		goto repeat;
+	}
+
+	/* clear status */
+	outb_p(0xFF, SMBHSTSTS);
+	ali1535_delay_loop();
+	
+	if(inb_p(SMBHSTSTS) != ALI1535_STS_IDLE) {
+		size = oldsize;
+		goto repeat;
+	}
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) {
+		result = 0;
+		goto EXIT;
+        }
+
+	switch (size) {
+	case ALI1535_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMBHSTDAT0);
+		ali1535_delay_loop();
+		break;
+	case ALI1535_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		ali1535_delay_loop();
+		break;
+	case ALI1535_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		ali1535_delay_loop();
+		break;
+	case ALI1535_BLOCK_DATA:
+		len = inb_p(SMBHSTDAT0);
+		ali1535_delay_loop();
+		if (len > 32)
+			len = 32;
+		data->block[0] = len;
+		{
+			int val;
+
+			val = inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR;
+			ali1535_delay_loop();
+			outb_p(val, SMBHSTTYP);	/* Reset SMBBLKDAT */
+			ali1535_delay_loop();
+		}
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+			ali1535_delay_loop();
+#ifdef DEBUG
+			printk
+			    ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n",
+			     len, i, data->block[i]);
+#endif	/* DEBUG */
+		}
+		break;
+	}
+EXIT:
+	return result;
+}
+
+static void ali1535_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void ali1535_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u32 ali1535_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-i2c SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= ali1535_access,
+	.functionality	= ali1535_func,
+};
+
+static struct i2c_adapter ali1535_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535,
+	.algo		= &smbus_algorithm,
+	.inc_use	= ali1535_inc,
+	.dec_use	= ali1535_dec,
+};
+
+
+static struct pci_device_id ali1535_ids[] __devinitdata = {
+	{
+	.vendor =	PCI_VENDOR_ID_AL,
+	.device =	PCI_DEVICE_ID_AL_M7101,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (ali1535_setup(dev)) {
+		printk
+		    ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x",
+		ali1535_smba);
+	return i2c_add_adapter(&ali1535_adapter);
+}
+
+static void __devexit ali1535_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&ali1535_adapter);
+	release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+}
+
+
+static struct pci_driver ali1535_driver = {
+	.name		= "ali1535 smbus",
+	.id_table	= ali1535_ids,
+	.probe		= ali1535_probe,
+	.remove		= __devexit_p(ali1535_remove),
+};
+
+static int __init i2c_ali1535_init(void)
+{
+	printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&ali1535_driver);
+}
+
+static void __exit i2c_ali1535_exit(void)
+{
+	pci_unregister_driver(&ali1535_driver);
+}
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, "
+     "Mark D. Studebaker <mdsxyz123@yahoo.com> and Dan Eaton <dan.eaton@rocketlogix.com>");
+MODULE_DESCRIPTION("ALI1535 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ali1535_init);
+module_exit(i2c_ali1535_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1563.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1563.c	(revision 3149)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali1563.c	(revision 3149)
@@ -0,0 +1,466 @@
+/**
+ *	i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
+ *
+ *	Copyright (C) 2004, 2005 Patrick Mochel, Chunhao Huang
+ *
+ *	The 1563 southbridge is deceptively similar to the 1533, with a
+ *	few notable exceptions. One of those happens to be the fact they
+ *	upgraded the i2c core to be 2.0 compliant, and happens to be almost
+ *	identical to the i2c controller found in the Intel 801 south
+ *	bridges.
+ *
+ *	This driver is based on a mix of the 15x3, 1535, and i801 drivers,
+ *	with a little help from the ALi 1563 spec.
+ *
+ *	Chunhao Huang / Winbond
+ *	Backport to linux-2.4 from linux-2.6 on Mar.2005
+ *
+ *	This file is released under the GPLv2
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+#ifndef PCI_DEVICE_ID_AL_M1563
+#define PCI_DEVICE_ID_AL_M1563      0x1563
+#endif
+
+#define ALI1563_MAX_TIMEOUT	500
+#define	ALI1563_SMBBA		0x80
+#define ALI1563_SMB_IOEN	1
+#define ALI1563_SMB_HOSTEN	2
+#define ALI1563_SMB_IOSIZE	16
+
+#define SMB_HST_STS	(ali1563_smba + 0)
+#define SMB_HST_CNTL1	(ali1563_smba + 1)
+#define SMB_HST_CNTL2	(ali1563_smba + 2)
+#define SMB_HST_CMD	(ali1563_smba + 3)
+#define SMB_HST_ADD	(ali1563_smba + 4)
+#define SMB_HST_DAT0	(ali1563_smba + 5)
+#define SMB_HST_DAT1	(ali1563_smba + 6)
+#define SMB_BLK_DAT	(ali1563_smba + 7)
+
+#define HST_STS_BUSY	0x01
+#define HST_STS_INTR	0x02
+#define HST_STS_DEVERR	0x04
+#define HST_STS_BUSERR	0x08
+#define HST_STS_FAIL	0x10
+#define HST_STS_DONE	0x80
+#define HST_STS_BAD	0x1c
+
+
+#define HST_CNTL1_TIMEOUT	0x80
+#define HST_CNTL1_LAST		0x40
+
+#define HST_CNTL2_KILL		0x04
+#define HST_CNTL2_START		0x40
+#define HST_CNTL2_QUICK		0x00
+#define HST_CNTL2_BYTE		0x01
+#define HST_CNTL2_BYTE_DATA	0x02
+#define HST_CNTL2_WORD_DATA	0x03
+#define HST_CNTL2_BLOCK		0x05
+#define HST_CNTL2_SIZEMASK	0x38
+
+static struct pci_driver ali1563_pci_driver;
+static unsigned short ali1563_smba;
+
+static int ali1563_transaction(struct i2c_adapter * a)
+{
+	u32 data;
+	int timeout;
+
+	printk(KERN_DEBUG "ali1563: Transaction (pre): STS=%02x, CNTL1=%02x, "
+		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
+		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
+		inb_p(SMB_HST_DAT1));
+
+	data = inb_p(SMB_HST_STS);
+	if (data & HST_STS_BAD) {
+		printk(KERN_WARNING "ali1563: Trying to reset busy device\n");
+		outb_p(data | HST_STS_BAD,SMB_HST_STS);
+		data = inb_p(SMB_HST_STS);
+		if (data & HST_STS_BAD){
+			return -EBUSY;
+		}
+	}
+	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
+
+	timeout = ALI1563_MAX_TIMEOUT;
+	do
+		i2c_delay(1);
+	while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout);
+
+	printk(KERN_DEBUG "ali1563: Transaction (post): STS=%02x, CNTL1=%02x, "
+		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
+		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
+		inb_p(SMB_HST_DAT1));
+
+	if (timeout && !(data & HST_STS_BAD)) {
+		return 0;
+	}
+
+	/* modified by Rudolf */
+	if (!timeout) {
+		printk(KERN_ERR "ali1563: Timeout - Trying to KILL transaction!\n");
+		/* Issue 'kill' to host controller */
+		outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2);
+		data = inb_p(SMB_HST_STS);		
+ 	}
+	
+	/* device error - probably missing ACK */	
+	if (data & HST_STS_DEVERR) {
+		printk(KERN_DEBUG "ali1563: Device error!\n");
+	}
+
+	/* bus collision */
+	if (data & HST_STS_BUSERR) {
+		printk(KERN_ERR "ali1563: Bus collision!\n");
+		/* Issue timeout, hoping it helps */
+		outb_p(HST_CNTL1_TIMEOUT,SMB_HST_CNTL1);
+	}
+
+	if (data & HST_STS_FAIL) {
+		printk(KERN_ERR "ali1563: Cleaning fail after KILL!\n");
+		outb_p(0x0,SMB_HST_CNTL2);
+	}
+
+	return -1;
+}
+
+static int ali1563_block_start(struct i2c_adapter * a)
+{
+	u32 data;
+	int timeout;
+
+	printk(KERN_DEBUG "ali1563: Block (pre): STS=%02x, CNTL1=%02x, "
+		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
+		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
+		inb_p(SMB_HST_DAT1));
+
+	data = inb_p(SMB_HST_STS);
+	if (data & HST_STS_BAD) {
+		printk(KERN_WARNING "ali1563: Trying to reset busy device\n");
+		outb_p(data | HST_STS_BAD,SMB_HST_STS);
+		data = inb_p(SMB_HST_STS);
+		if (data & HST_STS_BAD){
+			return -EBUSY;
+		}
+	}
+
+	/* Clear byte-ready bit */
+	outb_p(data | HST_STS_DONE, SMB_HST_STS);
+
+	/* Start transaction and wait for byte-ready bit to be set */
+	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
+
+	timeout = ALI1563_MAX_TIMEOUT;
+	do
+		i2c_delay(1);
+	while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout);
+
+	printk(KERN_DEBUG "ali1563: Block (post): STS=%02x, CNTL1=%02x, "
+		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
+		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
+		inb_p(SMB_HST_DAT1));
+
+	if (timeout && !(data & HST_STS_BAD)){
+		return 0;
+	}
+	printk(KERN_WARNING "ali1563: SMBus Error: %s%s%s%s%s\n",
+		timeout ? "Timeout " : "",
+		data & HST_STS_FAIL ? "Transaction Failed " : "",
+		data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
+		data & HST_STS_DEVERR ? "Device Error " : "",
+		!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
+	return -1;
+}
+
+static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw)
+{
+	int i, len;
+	int error = 0;
+
+	/* Do we need this? */
+	outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
+
+	if (rw == I2C_SMBUS_WRITE) {
+		len = data->block[0];
+		if (len < 1)
+			len = 1;
+		else if (len > 32)
+			len = 32;
+		outb_p(len,SMB_HST_DAT0);
+		outb_p(data->block[1],SMB_BLK_DAT);
+	} else
+		len = 32;
+
+	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2);
+
+	for (i = 0; i < len; i++) {
+		if (rw == I2C_SMBUS_WRITE) {
+			outb_p(data->block[i + 1], SMB_BLK_DAT);
+			if ((error = ali1563_block_start(a)))
+				break;
+		} else {
+			if ((error = ali1563_block_start(a)))
+				break;
+			if (i == 0) {
+				len = inb_p(SMB_HST_DAT0);
+				if (len < 1)
+					len = 1;
+				else if (len > 32)
+					len = 32;
+			}
+			data->block[i+1] = inb_p(SMB_BLK_DAT);
+		}
+	}
+	/* Do we need this? */
+	outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
+	return error;
+}
+
+static s32 ali1563_access(struct i2c_adapter * a, u16 addr,
+			  unsigned short flags, char rw, u8 cmd,
+			  int size, union i2c_smbus_data * data)
+{
+	int error = 0;
+	int timeout;
+	u32 reg;
+
+	for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) {
+		if (!(reg = inb_p(SMB_HST_STS) & HST_STS_BUSY))
+			break;
+	}
+	if (!timeout)
+		printk(KERN_WARNING "ali1563: SMBus not idle. HST_STS = %02x\n",reg);
+	outb_p(0xff,SMB_HST_STS);
+
+	/* Map the size to what the chip understands */
+	switch (size) {
+	case I2C_SMBUS_PROC_CALL:
+		printk(KERN_ERR "ali1563: I2C_SMBUS_PROC_CALL not supported!\n");
+		error = -EINVAL;
+		break;
+	case I2C_SMBUS_QUICK:
+		size = HST_CNTL2_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		size = HST_CNTL2_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		size = HST_CNTL2_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		size = HST_CNTL2_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		size = HST_CNTL2_BLOCK;
+		break;
+	}
+
+	outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD);
+	outb_p( (inb_p(SMB_HST_CNTL2)&~HST_CNTL2_SIZEMASK) | (size << 3), SMB_HST_CNTL2);
+
+	/* Write the command register */
+	switch(size) {
+	case HST_CNTL2_BYTE:
+		if (rw== I2C_SMBUS_WRITE)
+			/* outb_p(cmd, SMB_HST_CMD); */
+			outb_p(cmd, SMB_HST_DAT0); /* modify by Rudolf */
+		break;
+	case HST_CNTL2_BYTE_DATA:
+		outb_p(cmd, SMB_HST_CMD);
+		if (rw == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMB_HST_DAT0);
+		break;
+	case HST_CNTL2_WORD_DATA:
+		outb_p(cmd, SMB_HST_CMD);
+		if (rw == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMB_HST_DAT0);
+			outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1);
+		}
+		break;
+	case HST_CNTL2_BLOCK:
+		outb_p(cmd, SMB_HST_CMD);
+		error = ali1563_block(a,data,rw);
+		goto Done;
+	}
+
+	if ((error = ali1563_transaction(a))){
+		goto Done;
+	}
+
+	if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK)){
+		goto Done;
+	}
+
+	switch (size) {
+	case HST_CNTL2_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMB_HST_DAT0);
+		break;
+	case HST_CNTL2_BYTE_DATA:
+		data->byte = inb_p(SMB_HST_DAT0);
+		break;
+	case HST_CNTL2_WORD_DATA:
+		data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8);
+		break;
+	}
+Done:
+	return error;
+}
+
+static u32 ali1563_func(struct i2c_adapter * a)
+{
+	return ( I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA );
+}
+
+
+static void ali1563_enable(struct pci_dev * dev)
+{
+	u16 ctrl;
+
+	pci_read_config_word(dev,ALI1563_SMBBA,&ctrl);
+	ctrl |= 0x7;
+	pci_write_config_word(dev,ALI1563_SMBBA,ctrl);
+}
+
+static int __devinit ali1563_setup(struct pci_dev * dev)
+{
+	u16 ctrl;
+
+	pci_read_config_word(dev,ALI1563_SMBBA,&ctrl);
+	printk(KERN_DEBUG "ali1563: SMBus control = %04x\n",ctrl);
+
+	/* Check if device is even enabled first */
+	if (!(ctrl & ALI1563_SMB_IOEN)) {
+		printk(KERN_WARNING "ali1563: I/O space not enabled, trying manually\n");
+		ali1563_enable(dev);
+	}
+	if (!(ctrl & ALI1563_SMB_IOEN)) {
+		printk(KERN_WARNING "ali1563: I/O space still not enabled, giving up\n");
+		goto Err;
+	}
+	if (!(ctrl & ALI1563_SMB_HOSTEN)) {
+		printk(KERN_WARNING "ali1563: Host Controller not enabled\n");
+		goto Err;
+	}
+
+	/* SMB I/O Base in high 12 bits and must be aligned with the
+	 * size of the I/O space. */
+	ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1);
+	if (!ali1563_smba) {
+		printk(KERN_WARNING "ali1563: ali1563_smba Uninitialized\n");
+		goto Err;
+	}
+	if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE,
+			    ali1563_pci_driver.name)) {
+		printk(KERN_WARNING "ali1563: Could not allocate I/O space");
+		goto Err;
+	}
+
+	return 0;
+Err:
+	return -ENODEV;
+}
+
+static void ali1563_shutdown(struct pci_dev *dev)
+{
+	release_region(ali1563_smba,ALI1563_SMB_IOSIZE);
+}
+
+static void ali1563_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void ali1563_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algorithm ali1563_algorithm = {
+	.name		= "Non-i2c SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= ali1563_access,
+	.functionality	= ali1563_func,
+};
+
+static struct i2c_adapter ali1563_adapter = {
+	.algo		= &ali1563_algorithm,
+	.inc_use	= ali1563_inc,
+	.dec_use	= ali1563_dec,
+};
+
+static int __devinit ali1563_probe(struct pci_dev * dev,
+				const struct pci_device_id * id_table)
+{
+	int error;
+
+	if ((error = ali1563_setup(dev))){
+		return error;
+	}
+	sprintf(ali1563_adapter.name,"SMBus ALi 1563 Adapter @ %04x",
+		ali1563_smba);
+	if ((error = i2c_add_adapter(&ali1563_adapter)))
+		ali1563_shutdown(dev);
+	printk(KERN_DEBUG "%s: Returning %d\n",__FUNCTION__,error);
+	return error;
+}
+
+static void __devexit ali1563_remove(struct pci_dev * dev)
+{
+	i2c_del_adapter(&ali1563_adapter);
+	ali1563_shutdown(dev);
+}
+
+static struct pci_device_id ali1563_ids[] __devinitdata = {
+	{
+	.vendor =       PCI_VENDOR_ID_AL,
+	.device =       PCI_DEVICE_ID_AL_M1563,
+	.subvendor =    PCI_ANY_ID,
+	.subdevice =    PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static struct pci_driver ali1563_pci_driver = {
+ 	.name		= "ali1563 smbus",
+	.id_table	= ali1563_ids,
+ 	.probe		= ali1563_probe,
+	.remove		= __devexit_p(ali1563_remove),
+};
+
+static int __init ali1563_init(void)
+{
+	printk("i2c-ali1563.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&ali1563_pci_driver);
+}
+
+module_init(ali1563_init);
+
+static void __exit ali1563_exit(void)
+{
+	pci_unregister_driver(&ali1563_pci_driver);
+}
+
+module_exit(ali1563_exit);
+
+MODULE_AUTHOR("Patrick Mochel <mochel@digitalimplant.org>, Chunhao Huang");
+MODULE_DESCRIPTION("ALi M1563 southbridge driver for linux-2.4, backported by Chunhao Huang from linux-2.6");
+MODULE_LICENSE("GPL");
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-virtual.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-virtual.c	(revision 2826)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-virtual.c	(revision 2826)
@@ -0,0 +1,430 @@
+/*
+ * i2c-virtual.c - Virtual I2C bus driver.
+ *
+ * Simplifies access to complex multiplexed I2C bus topologies, by presenting
+ * each multiplexed bus segment as a virtual I2C adapter.  Supports multi-level
+ * mux'ing (mux behind a mux), as well as arbitration for exclusive bus access
+ * for those systems which require it (some bladed chassis for example).
+ *
+ * Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
+ *
+ * Based on:
+ *    i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
+ * which was:
+ *    Adapted from i2c-adap-ibm_ocp.c 
+ *    Original file Copyright 2000-2002 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include "i2c-virtual.h"
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#ifdef DEBUG
+# define DBG(x) x
+#else
+# define DBG(x)	{}
+#endif
+
+#define I2C_VIRT_PROCMAP 0	/* 1 for /proc/driver/i2c/virtual_i2c_map */
+
+#define VIRT_TIMEOUT		(HZ/2)		/* 500msec */
+#define VIRT_RETRIES		3		
+
+/* exclusive access to the bus */
+#define I2C_LOCK(adap) down(&adap->bus)
+#define I2C_UNLOCK(adap) up(&adap->bus)
+
+#if I2C_VIRT_PROCMAP
+/* pointer to the list of i2c_bus_mappings */
+static LIST_HEAD(i2c_map_list);
+struct i2c_bus_mapping {
+	struct i2c_adapter	*parent;	
+	struct i2c_adapter	*virt;
+	unsigned long		mux_addr;	
+	unsigned long		mux_val;
+	struct list_head	list; 
+};
+static void *i2c_virt_add_map(struct i2c_adapter *parent,
+                              struct i2c_adapter *virt,
+                              unsigned long mux_addr,
+                              unsigned long mux_val);
+#endif
+
+/* First the I2C 'algorithm' driver code: */
+
+/*
+ * Description: acquire exclusive access (if needed), select the mux,
+ * perform the transfer on the parent i2c adapter, deselect mux and drop
+ * exclusive access.
+ */
+static int
+i2c_virt_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct i2c_virt_priv 	*priv = (struct i2c_virt_priv *)adap->data;
+	struct i2c_adapter 	*parent = priv->parent_adap;
+	int ret;
+
+
+#ifdef I2C_REQUIRE_ARBITRATION
+	/* Acquire exclusive access to a shared I2C bus _before_ taking the
+           local I2C lock to prevent stalling local I2C transactions while
+           waiting for exclusive access.
+	 */
+	if(parent->algo->acquire_exclusive) 
+		if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
+			return ret;
+#endif
+	/* Grab the lock for the parent adapter.  We already hold the lock for
+           the virtual adapter.  Then select the right mux port and perform
+           the transfer.
+	 */
+	DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
+                   __FUNCTION__, (unsigned long)parent));
+	I2C_LOCK(parent);
+	if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
+            ret = parent->algo->master_xfer(parent, msgs, num);
+        }
+	(void) priv->mux.deselect(parent, &priv->mux);
+	I2C_UNLOCK(parent);
+	DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
+                   __FUNCTION__, (unsigned long)parent));
+
+#ifdef I2C_REQUIRE_ARBITRATION
+    {
+	int ret2;
+	/* Release exclusive bus access */
+	if(parent->algo->release_exclusive) 
+		if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
+			return ret2;
+    }
+#endif
+	return ret;
+}
+
+static int
+i2c_virt_smbus_xfer(struct i2c_adapter *adap, u16 addr, 
+                    unsigned short flags, char read_write,
+                    u8 command, int size, union i2c_smbus_data * data)
+{
+	struct i2c_virt_priv 	*priv = (struct i2c_virt_priv *)adap->data;
+	struct i2c_adapter 	*parent = priv->parent_adap;
+	int ret;
+
+#ifdef I2C_REQUIRE_ARBITRATION
+	/* Acquire exclusive access to a shared I2C bus _before_ taking the
+           local I2C lock to prevent stalling local I2C transactions while
+           waiting for exclusive access.
+	 */
+	if(parent->algo->acquire_exclusive) 
+		if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
+			return ret;
+#endif
+	/* Grab the lock for the parent adapter.  We already hold the lock for
+           the virtual adapter.  Then select the right mux port and perform
+           the transfer.
+	 */
+	DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
+                   __FUNCTION__, (unsigned long)parent));
+	I2C_LOCK(parent);
+	if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
+            ret = parent->algo->smbus_xfer(parent, addr, flags,
+                                           read_write, command, size, data);
+        }
+	(void) priv->mux.deselect(parent, &priv->mux);
+	I2C_UNLOCK(parent);
+	DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
+                   __FUNCTION__, (unsigned long)parent));
+
+#ifdef I2C_REQUIRE_ARBITRATION
+    {
+	int ret2;
+	/* Release exclusive bus access */
+	if(parent->algo->release_exclusive) 
+		if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
+			return ret2;
+    }
+#endif
+	return ret;
+}
+
+/*
+ * Description: Implements device specific ioctls.  
+ * We don't support any yet.
+ */
+static int
+i2c_virt_algo_control(struct i2c_adapter *adap, unsigned int cmd,
+                      unsigned long arg)
+{
+	return 0;
+}
+
+/* Virtual adapter functionality; returns functionality of parent adapter.
+ * OK if parent itself is virtual, as this will go all the way up to the
+ * real adapter.
+ */
+static u32
+i2c_virt_functionality(struct i2c_adapter *adap)
+{
+	struct i2c_virt_priv 	*priv = (struct i2c_virt_priv *)adap->data;
+	struct i2c_adapter 	*parent = priv->parent_adap;
+
+	return parent->algo->functionality(parent);
+}
+
+/* ===================================================================== */
+
+/* The 'adapter' driver code: */
+
+static int
+i2c_virt_reg(struct i2c_client *client)
+{
+	return 0;
+}
+static int
+i2c_virt_unreg(struct i2c_client *client)
+{
+	return 0;
+}
+
+
+/*
+ * Called to create a 'virtual' i2c bus which represents a multiplexed bus
+ * segment.  Client and mux_val are passed to the select and deselect
+ * callback functions to perform hardware-specific mux control.
+ */
+struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap, 
+                                            struct i2c_client *client,
+                                            unsigned long mux_val, 
+                                            void *select_cb,
+                                            void *deselect_cb)
+{
+	struct i2c_adapter *adap;
+	struct i2c_virt_priv *priv;
+	struct i2c_algorithm *algo;
+
+	if (!(adap = kmalloc(sizeof (struct i2c_adapter)
+                             + sizeof(struct i2c_virt_priv)
+                             + sizeof(struct i2c_algorithm), 
+                             GFP_KERNEL))) {
+		printk(KERN_ERR "i2c_virt_register_adap: Failed allocation\n");
+		return NULL;
+	}
+
+	memset(adap, 0, sizeof(struct i2c_adapter)
+                      + sizeof(struct i2c_virt_priv)
+		      + sizeof(struct i2c_algorithm));
+        priv = (struct i2c_virt_priv *) (adap+1);
+        algo = (struct i2c_algorithm *) (priv+1);
+
+        /* Set up private adapter data */
+	priv->parent_adap = parent_adap;
+	priv->mux.client = client;
+	priv->mux.addr = client->addr;
+	priv->mux.value = mux_val;
+	priv->mux.select = select_cb;
+	priv->mux.deselect = deselect_cb;
+
+        /* Need to do algo dynamically because we don't know ahead
+           of time what sort of physical adapter we'll be dealing with.
+        */
+        algo->id = I2C_ALGO_VIRT;
+        strcpy(algo->name, "Virtual I2C algorithm driver");
+        algo->master_xfer = (parent_adap->algo->master_xfer
+                             ? i2c_virt_master_xfer : NULL);
+        algo->smbus_xfer = (parent_adap->algo->smbus_xfer
+                             ? i2c_virt_smbus_xfer : NULL);
+        algo->slave_send = NULL;
+        algo->slave_recv = NULL;
+        algo->algo_control = i2c_virt_algo_control;
+        algo->functionality = i2c_virt_functionality;
+#ifdef I2C_REQUIRE_ARBITRATION
+	algo->acquire_exclusive = NULL;
+	algo->release_exclusive = NULL;
+#endif
+
+        /* Now fill out new adapter structure */
+	adap->inc_use = NULL;
+	adap->dec_use = NULL;
+	snprintf(adap->name, sizeof(adap->name),
+                 "Virtual I2C (i2c-%d, mux %02lx:%02lx)",
+                 i2c_adapter_id(parent_adap),
+                 (unsigned long)client->addr, mux_val);
+	adap->id = I2C_HW_VIRT | algo->id;
+	adap->algo = algo;
+        adap->algo_data = NULL;		/* XXX: Use this?? */
+	adap->client_register = i2c_virt_reg;
+	adap->client_unregister = i2c_virt_unreg;
+	adap->data = priv;
+	adap->flags = 0;
+	adap->timeout = VIRT_TIMEOUT;
+	adap->retries = VIRT_RETRIES;
+
+	DBG(printk(KERN_DEBUG "%s: Adding virt bus %0lX\n",
+                   __FUNCTION__, (unsigned long)adap));
+
+	if (i2c_add_adapter_nolock(adap) < 0) {
+            kfree(adap);
+            return NULL;
+        }
+
+	printk(KERN_NOTICE "i2c%d: Virtual I2C bus "
+		"(Physical bus i2c%d, multiplexer %02lx port %ld)\n",
+		i2c_adapter_id(adap), i2c_adapter_id(parent_adap), 
+		priv->mux.addr, mux_val);
+
+#if I2C_VIRT_PROCMAP
+        i2c_virt_add_map(priv->parent_adap, adap,
+                         priv->mux.addr, priv->value);
+#endif
+
+	MOD_INC_USE_COUNT;
+	DBG(printk(KERN_DEBUG "%s: Added virt bus %0lX\n",
+                   __FUNCTION__, (unsigned long)adap));
+
+	return adap;
+}
+
+int i2c_virt_remove_adapter(struct i2c_adapter *adap)
+{
+	int ret;
+
+	DBG(printk(KERN_DEBUG "%s: Removing virt bus %0lX\n",
+                   __FUNCTION__, (unsigned long)adap));
+
+	if ((ret = i2c_del_adapter_nolock(adap)) < 0)
+		return ret;
+        kfree(adap);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+#if I2C_VIRT_PROCMAP
+/* Dubious stuff to implement a /proc/driver/i2c/virtual_i2c_map
+ * listing of all virtual busses.
+ *
+ * XXX: LOCKING???
+ */
+static int i2c_virt_read_proc(char *page, char **start, off_t off,
+                         int count, int *eof, void *data)
+{
+	int len;
+	char *buf = page;
+	struct list_head *l;
+	struct i2c_bus_mapping *pmap;
+
+        /* XXX: BUFFER BOUNDS CHECKING? */
+
+        buf += sprintf(buf, "i2c bus_mapping\n");
+
+	list_for_each(l, &i2c_map_list) {
+		pmap = list_entry(l, struct i2c_bus_mapping, list);
+
+		if(pmap)
+        		buf += sprintf(buf, "base=iic%d  mux_addr=%02hx, mux_val=%02hx  =>  iic%d\n", 
+				i2c_adapter_id(pmap->parent),
+                                pmap->mux_addr, pmap->mux_val, 
+				i2c_adapter_id(pmap->virt));
+	}
+
+        len = buf - page;
+        if (len <= off+count) *eof = 1;
+        *start = page + off;
+        len -= off;
+        if (len>count) len = count;
+        if (len<0) len = 0;
+        return len;
+}
+
+
+static void *i2c_virt_add_map(struct i2c_adapter *parent,
+                              struct i2c_adapter *virt,
+                              unsigned long mux_addr,
+                              unsigned long mux_val)
+{
+	struct i2c_bus_mapping *pmap;
+
+	if((pmap = kmalloc(sizeof(struct i2c_bus_mapping), GFP_KERNEL))
+           == NULL) {
+		printk(KERN_ERR "%s: Failed to allocate bus mapping\n",
+                       __FUNCTION__);
+		return;
+	}
+	pmap->virt = virt;
+	pmap->parent = parent;
+	pmap->mux_addr = mux_addr;
+	pmap->mux_val = mux_val;
+
+        if (list_empty(&i2c_map_list)) {
+            /* If first time, ensure proc entry exists */
+            /* XXX: Maybe do this with module_init() */
+            create_proc_read_entry("driver/i2c/virtual_i2c_map", 0, NULL,
+                               &i2c_virt_read_proc, NULL);
+        }
+	list_add_tail(&pmap->list, &i2c_map_list);
+}
+
+
+#if 0 /* Not used by anything, but kept for a while in case */
+
+/* Called to find out which i2c bus to use to get to a specific bus segment.
+ */
+struct i2c_adapter *i2c_lookup_adapter(struct i2c_adapter *base,
+                                       unsigned long mux_addr, 
+                                       unsigned long mux_val)
+{	
+	struct list_head *l;
+	struct i2c_bus_mapping *pmap;
+
+	list_for_each(l, &i2c_map_list) {
+		pmap = list_entry(l, struct i2c_bus_mapping, list);
+		if(pmap->parent == base
+                   && pmap->mux_addr == mux_addr
+                   && pmap->mux_val == mux_val)
+			return pmap->virt;
+	}
+	return NULL;	/* none found */
+}
+
+EXPORT_SYMBOL(i2c_lookup_adapter);
+#endif /* 0 */
+
+
+#endif /* I2C_VIRT_PROCMAP */
+
+EXPORT_SYMBOL(i2c_virt_create_adapter);
+EXPORT_SYMBOL(i2c_virt_remove_adapter);
+
+MODULE_AUTHOR("Ken Harrenstien (from Brian Kuschak)");
+MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses");
+MODULE_LICENSE("GPL");
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis5595.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis5595.c	(revision 3149)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis5595.c	(revision 3149)
@@ -0,0 +1,495 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.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.
+*/
+
+/* Note: we assume there can only be one SIS5595 with one SMBus interface */
+
+/*
+   Note: all have mfr. ID 0x1039.
+   SUPPORTED		PCI ID		
+	5595		0008
+
+   Note: these chips contain a 0008 device which is incompatible with the
+         5595. We recognize these by the presence of the listed
+         "blacklist" PCI ID and refuse to load.
+
+   NOT SUPPORTED	PCI ID		BLACKLIST PCI ID	
+	 540		0008		0540
+	 550		0008		0550
+	5513		0008		5511
+	5581		0008		5597
+	5582		0008		5597
+	5597		0008		5597
+	5598		0008		5597/5598
+	 630		0008		0630
+	 645		0008		0645
+	 646		0008		0646
+	 648		0008		0648
+	 650		0008		0650
+	 651		0008		0651
+	 730		0008		0730
+	 735		0008		0735
+	 745		0008		0745
+	 746		0008		0746
+*/
+
+/* TO DO: 
+ * Add Block Transfers (ugly, but supported by the adapter)
+ * Add adapter resets
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+
+static int blacklist[] = {
+			PCI_DEVICE_ID_SI_540,
+			PCI_DEVICE_ID_SI_550,
+			PCI_DEVICE_ID_SI_630,
+			PCI_DEVICE_ID_SI_730,
+			PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
+						  that ID shows up in other chips so we
+						  use the 5511 ID for recognition */
+			PCI_DEVICE_ID_SI_5597,
+			PCI_DEVICE_ID_SI_5598,
+			0x645,
+			0x646,
+			0x648,
+			0x650,
+			0x651,
+			0x735,
+			0x745,
+			0x746,
+			0 };
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* SIS5595 SMBus registers */
+#define SMB_STS_LO 0x00
+#define SMB_STS_HI 0x01
+#define SMB_CTL_LO 0x02
+#define SMB_CTL_HI 0x03
+#define SMB_ADDR   0x04
+#define SMB_CMD    0x05
+#define SMB_PCNT   0x06
+#define SMB_CNT    0x07
+#define SMB_BYTE   0x08
+#define SMB_DEV    0x10
+#define SMB_DB0    0x11
+#define SMB_DB1    0x12
+#define SMB_HAA    0x13
+
+/* PCI Address Constants */
+#define SMB_INDEX  0x38
+#define SMB_DAT    0x39
+#define SIS5595_ENABLE_REG 0x40
+#define ACPI_BASE  0x90
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* SIS5595 constants */
+#define SIS5595_QUICK      0x00
+#define SIS5595_BYTE       0x02
+#define SIS5595_BYTE_DATA  0x04
+#define SIS5595_WORD_DATA  0x06
+#define SIS5595_PROC_CALL  0x08
+#define SIS5595_BLOCK_DATA 0x0A
+
+/* insmod parameters */
+
+/* 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 i2c controller");
+
+static int sis5595_transaction(void);
+
+static struct pci_driver sis5595_driver;
+static unsigned short sis5595_base = 0;
+
+static u8 sis5595_read(u8 reg)
+{
+	outb(reg, sis5595_base + SMB_INDEX);
+	return inb(sis5595_base + SMB_DAT);
+}
+
+static void sis5595_write(u8 reg, u8 data)
+{
+	outb(reg, sis5595_base + SMB_INDEX);
+	outb(data, sis5595_base + SMB_DAT);
+}
+
+
+/* Detect whether a SIS5595 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+int sis5595_setup(struct pci_dev *SIS5595_dev)
+{
+	u16 a;
+	u8 val;
+	int *i;
+
+	/* Look for imposters */
+	for(i = blacklist; *i != 0; i++) {
+		if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) {
+			printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i);
+			return -ENODEV;
+		}
+	}
+
+/* Determine the address of the SMBus areas */
+	pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
+	if(sis5595_base == 0 && force_addr == 0) {
+		printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+	if(force_addr)
+		sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
+#ifdef DEBUG
+	printk("ACPI Base address: %04x\n", sis5595_base);
+#endif
+	/* NB: We grab just the two SMBus registers here, but this may still
+	 * interfere with ACPI :-(  */
+	if (check_region(sis5595_base + SMB_INDEX, 2)) {
+		printk
+		    ("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n",
+		     sis5595_base + SMB_INDEX,
+		     sis5595_base + SMB_INDEX + 1);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(SIS5595_dev, ACPI_BASE, &a))
+			return -ENODEV;
+		if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
+			/* doesn't work for some chips! */
+			printk("i2c-sis5595.o: force address failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
+		return -ENODEV;
+	if((val & 0x80) == 0) {
+		printk("sis5595.o: enabling ACPI\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG,
+		                      val | 0x80))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
+			return -ENODEV;
+		if((val & 0x80) == 0) {	/* doesn't work for some chips? */
+			printk("sis5595.o: ACPI enable failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(sis5595_base + SMB_INDEX, 2, sis5595_driver.name);
+	return(0);
+}
+
+
+/* Another internally used function */
+int sis5595_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+#ifdef DEBUG
+		printk("i2c-sis5595.o: SMBus busy (%04x). Resetting...\n",
+		       temp);
+#endif
+		sis5595_write(SMB_STS_LO, temp & 0xff);
+		sis5595_write(SMB_STS_HI, temp >> 8);
+		if (
+		    (temp =
+		     sis5595_read(SMB_STS_LO) +
+		     (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
+#ifdef DEBUG
+			printk("i2c-sis5595.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("i2c-sis5595.o: Successfull!\n");
+#endif
+		}
+	}
+
+	/* start the transaction by setting bit 4 */
+	sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = sis5595_read(SMB_STS_LO);
+	} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+#ifdef DEBUG
+		printk("i2c-sis5595.o: SMBus Timeout!\n");
+#endif
+		result = -1;
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-sis5595.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x20) {
+		result = -1;
+		printk
+		    ("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n"
+		     "reset (or not...)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+		sis5595_write(SMB_STS_LO, temp & 0xff);
+		sis5595_write(SMB_STS_HI, temp >> 8);
+	}
+
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+
+#ifdef DEBUG
+		printk
+		    ("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+#endif
+	}
+	return result;
+}
+
+/* Return -1 on error. */
+s32 sis5595_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		size = SIS5595_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		if (read_write == I2C_SMBUS_WRITE)
+			sis5595_write(SMB_CMD, command);
+		size = SIS5595_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis5595_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE)
+			sis5595_write(SMB_BYTE, data->byte);
+		size = SIS5595_BYTE_DATA;
+		break;
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_WORD_DATA:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis5595_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			sis5595_write(SMB_BYTE, data->word & 0xff);
+			sis5595_write(SMB_BYTE + 1,
+				      (data->word & 0xff00) >> 8);
+		}
+		size =
+		    (size ==
+		     I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL :
+		    SIS5595_WORD_DATA;
+		break;
+/*
+	case I2C_SMBUS_BLOCK_DATA:
+		printk("sis5595.o: Block data not yet implemented!\n");
+		return -1;
+		break;
+*/
+	default:
+		printk
+		    (KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
+
+	if (sis5595_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((size != SIS5595_PROC_CALL) &&
+	    ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
+		return 0;
+
+
+	switch (size) {
+	case SIS5595_BYTE:	/* Where is the result put? I assume here it is in
+				   SMB_DATA but it might just as well be in the
+				   SMB_CMD. No clue in the docs */
+	case SIS5595_BYTE_DATA:
+		data->byte = sis5595_read(SMB_BYTE);
+		break;
+	case SIS5595_WORD_DATA:
+	case SIS5595_PROC_CALL:
+		data->word =
+		    sis5595_read(SMB_BYTE) +
+		    (sis5595_read(SMB_BYTE + 1) << 8);
+		break;
+	}
+	return 0;
+}
+
+static void sis5595_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void sis5595_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u32 sis5595_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis5595_access,
+	.functionality	= sis5595_func,
+};
+
+static struct i2c_adapter sis5595_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595,
+	.algo		= &smbus_algorithm,
+	.inc_use	= sis5595_inc,
+	.dec_use	= sis5595_dec,
+};
+
+
+static struct pci_device_id sis5595_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_SI,
+		.device =	PCI_DEVICE_ID_SI_503,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+
+	if (sis5595_setup(dev)) {
+		printk
+		    ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n");
+
+		return -ENODEV;
+	}
+
+	sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
+		sis5595_base + SMB_INDEX);
+	i2c_add_adapter(&sis5595_adapter);
+
+	return 0;
+}
+
+static void __devexit sis5595_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&sis5595_adapter);
+	release_region(sis5595_base + SMB_INDEX, 2);
+}
+
+
+static struct pci_driver sis5595_driver = {
+	.name		= "sis5595 smbus",
+	.id_table	= sis5595_ids,
+	.probe		= sis5595_probe,
+	.remove		= __devexit_p(sis5595_remove),
+};
+
+static int __init i2c_sis5595_init(void)
+{
+	printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&sis5595_driver);
+}
+
+
+static void __exit i2c_sis5595_exit(void)
+{
+	pci_unregister_driver(&sis5595_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("SIS5595 SMBus driver");
+
+module_init(i2c_sis5595_init);
+module_exit(i2c_sis5595_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali15x3.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali15x3.c	(revision 3149)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ali15x3.c	(revision 3149)
@@ -0,0 +1,548 @@
+/*
+    ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com> 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 is the driver for the SMB Host controller on
+    Acer Labs Inc. (ALI) M1541 and M1543C South Bridges.
+
+    The M1543C is a South bridge for desktop systems.
+    The M1533 is a South bridge for portable systems.
+    They are part of the following ALI chipsets:
+       "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge
+       with AGP and 100MHz CPU Front Side bus
+       "Aladdin V": Includes the M1541 Socket 7 North bridge
+       with AGP and 100MHz CPU Front Side bus
+       "Aladdin IV": Includes the M1541 Socket 7 North bridge
+       with host bus up to 83.3 MHz.
+    For an overview of these chips see http://www.acerlabs.com
+
+    The M1533/M1543C devices appear as FOUR separate devices
+    on the PCI bus. An output of lspci will show something similar
+    to the following:
+
+	00:02.0 USB Controller: Acer Laboratories Inc. M5237
+	00:03.0 Bridge: Acer Laboratories Inc. M7101
+	00:07.0 ISA bridge: Acer Laboratories Inc. M1533
+	00:0f.0 IDE interface: Acer Laboratories Inc. M5229
+
+    The SMB controller is part of the 7101 device, which is an
+    ACPI-compliant Power Management Unit (PMU).
+
+    The whole 7101 device has to be enabled for the SMB to work.
+    You can't just enable the SMB alone.
+    The SMB and the ACPI have separate I/O spaces.
+    We make sure that the SMB is enabled. We leave the ACPI alone.
+
+    This driver controls the SMB Host only.
+    The SMB Slave controller on the M15X3 is not enabled.
+
+    This driver does not use interrupts.
+*/
+
+/* Note: we assume there can only be one ALI15X3, with one SMBus interface */
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+/* ALI15X3 SMBus address offsets */
+#define SMBHSTSTS	(0 + ali15x3_smba)
+#define SMBHSTCNT	(1 + ali15x3_smba)
+#define SMBHSTSTART	(2 + ali15x3_smba)
+#define SMBHSTCMD	(7 + ali15x3_smba)
+#define SMBHSTADD	(3 + ali15x3_smba)
+#define SMBHSTDAT0	(4 + ali15x3_smba)
+#define SMBHSTDAT1	(5 + ali15x3_smba)
+#define SMBBLKDAT	(6 + ali15x3_smba)
+
+/* PCI Address Constants */
+#define SMBCOM		0x004
+#define SMBBA		0x014
+#define SMBATPC		0x05B	/* used to unlock xxxBA registers */
+#define SMBHSTCFG	0x0E0
+#define SMBSLVC		0x0E1
+#define SMBCLK		0x0E2
+#define SMBREV		0x008
+
+/* Other settings */
+#define MAX_TIMEOUT		200	/* times 1/100 sec */
+#define ALI15X3_SMB_IOSIZE	32
+
+/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB.
+   We don't use these here. If the bases aren't set to some value we
+   tell user to upgrade BIOS and we fail.
+*/
+#define ALI15X3_SMB_DEFAULTBASE	0xE800
+
+/* ALI15X3 address lock bits */
+#define ALI15X3_LOCK		0x06
+
+/* ALI15X3 command constants */
+#define ALI15X3_ABORT		0x02
+#define ALI15X3_T_OUT		0x04
+#define ALI15X3_QUICK		0x00
+#define ALI15X3_BYTE		0x10
+#define ALI15X3_BYTE_DATA	0x20
+#define ALI15X3_WORD_DATA	0x30
+#define ALI15X3_BLOCK_DATA	0x40
+#define ALI15X3_BLOCK_CLR	0x80
+
+/* ALI15X3 status register bits */
+#define ALI15X3_STS_IDLE	0x04
+#define ALI15X3_STS_BUSY	0x08
+#define ALI15X3_STS_DONE	0x10
+#define ALI15X3_STS_DEV		0x20	/* device error */
+#define ALI15X3_STS_COLL	0x40	/* collision or no response */
+#define ALI15X3_STS_TERM	0x80	/* terminated by abort */
+#define ALI15X3_STS_ERR		0xE0	/* all the bad error bits */
+
+
+/* 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 i2c controller");
+
+static struct pci_driver ali15x3_driver;
+static unsigned short ali15x3_smba = 0;
+
+static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
+{
+	u16 a;
+	unsigned char temp;
+
+	/* Check the following things:
+		- SMB I/O address is initialized
+		- Device is enabled
+		- We can use the addresses
+	*/
+
+	/* Unlock the register.
+	   The data sheet says that the address registers are read-only
+	   if the lock bits are 1, but in fact the address registers
+	   are zero unless you clear the lock bits.
+	*/
+	pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp);
+	if (temp & ALI15X3_LOCK) {
+		temp &= ~ALI15X3_LOCK;
+		pci_write_config_byte(ALI15X3_dev, SMBATPC, temp);
+	}
+
+	/* Determine the address of the SMBus area */
+	pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba);
+	ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1));
+	if (ali15x3_smba == 0 && force_addr == 0) {
+		dev_err(ALI15X3_dev, "ALI15X3_smb region uninitialized "
+			"- upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+	if(force_addr)
+		ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1);
+
+	if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE,
+			    ali15x3_driver.name)) {
+		dev_err(ALI15X3_dev,
+			"ALI15X3_smb region 0x%x already in use!\n",
+			ali15x3_smba);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		dev_info(ALI15X3_dev, "forcing ISA address 0x%04X\n",
+			ali15x3_smba);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(ALI15X3_dev, SMBBA, &a))
+			return -ENODEV;
+		if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) {
+			/* make sure it works */
+			dev_err(ALI15X3_dev,
+				"force address failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+	/* check if whole device is enabled */
+	pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp);
+	if ((temp & 1) == 0) {
+		dev_info(ALI15X3_dev, "enabling SMBus device\n");
+		pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01);
+	}
+
+	/* Is SMB Host controller enabled? */
+	pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp);
+	if ((temp & 1) == 0) {
+		dev_info(ALI15X3_dev, "enabling SMBus controller\n");
+		pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01);
+	}
+
+	/* set SMB clock to 74KHz as recommended in data sheet */
+	pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20);
+
+	/*
+	  The interrupt routing for SMB is set up in register 0x77 in the
+	  1533 ISA Bridge device, NOT in the 7101 device.
+	  Don't bother with finding the 1533 device and reading the register.
+	if ((....... & 0x0F) == 1)
+		dev_dbg(ALI15X3_dev, "ALI15X3 using Interrupt 9 for SMBus.\n");
+	*/
+	pci_read_config_byte(ALI15X3_dev, SMBREV, &temp);
+	dev_dbg(ALI15X3_dev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(ALI15X3_dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba);
+
+	return 0;
+}
+
+/* Another internally used function */
+static int ali15x3_transaction(struct i2c_adapter *adap)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(adap, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
+		inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+		inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+
+	/* get status */
+	temp = inb_p(SMBHSTSTS);
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* Check the busy bit first */
+	if (temp & ALI15X3_STS_BUSY) {
+	/*
+	   If the host controller is still busy, it may have timed out in the
+	   previous transaction, resulting in a "SMBus Timeout" Dev.
+	   I've tried the following to reset a stuck busy bit.
+		1. Reset the controller with an ABORT command.
+		   (this doesn't seem to clear the controller if an external
+		   device is hung)
+		2. Reset the controller and the other SMBus devices with a
+		   T_OUT command.  (this clears the host busy bit if an
+		   external device is hung, but it comes back upon a new access
+		   to a device)
+		3. Disable and reenable the controller in SMBHSTCFG
+	   Worst case, nothing seems to work except power reset.
+	*/
+	/* Abort - reset the host controller */
+	/*
+	   Try resetting entire SMB bus, including other devices -
+	   This may not work either - it clears the BUSY bit but
+	   then the BUSY bit may come back on when you try and use the chip again.
+	   If that's the case you are stuck.
+	*/
+		dev_info(adap, "Resetting entire SMB Bus to "
+			"clear busy condition (%02x)\n", temp);
+		outb_p(ALI15X3_T_OUT, SMBHSTCNT);
+		temp = inb_p(SMBHSTSTS);
+	}
+
+	/* now check the error bits and the busy bit */
+	if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
+		/* do a clear-on-write */
+		outb_p(0xFF, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) &
+		    (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
+			/* this is probably going to be correctable only by a power reset
+			   as one of the bits now appears to be stuck */
+			/* This may be a bus or device with electrical problems. */
+			dev_err(adap, "SMBus reset failed! (0x%02x) - "
+				"controller or device on bus is probably hung\n",
+				temp);
+			return -1;
+		}
+	} else {
+		/* check and clear done bit */
+		if (temp & ALI15X3_STS_DONE) {
+			outb_p(temp, SMBHSTSTS);
+		}
+	}
+
+	/* start the transaction by writing anything to the start register */
+	outb_p(0xFF, SMBHSTSTART);
+
+	/* We will always wait for a fraction of a second! */
+	timeout = 0;
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE)))
+		 && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		dev_err(adap, "SMBus Timeout!\n");
+	}
+
+	if (temp & ALI15X3_STS_TERM) {
+		result = -1;
+		dev_dbg(adap, "Error: Failed bus transaction\n");
+	}
+
+	/*
+	  Unfortunately the ALI SMB controller maps "no response" and "bus
+	  collision" into a single bit. No reponse is the usual case so don't
+	  do a printk.
+	  This means that bus collisions go unreported.
+	*/
+	if (temp & ALI15X3_STS_COLL) {
+		result = -1;
+		dev_dbg(adap,
+			"Error: no response or bus collision ADD=%02x\n",
+			inb_p(SMBHSTADD));
+	}
+
+	/* haven't ever seen this */
+	if (temp & ALI15X3_STS_DEV) {
+		result = -1;
+		dev_err(adap, "Error: device error\n");
+	}
+	dev_dbg(adap, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
+		inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+		inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write, u8 command,
+		   int size, union i2c_smbus_data * data)
+{
+	int i, len;
+	int temp;
+	int timeout;
+
+	/* clear all the bits (clear-on-write) */
+	outb_p(0xFF, SMBHSTSTS);
+	/* make sure SMBus is idle */
+	temp = inb_p(SMBHSTSTS);
+	for (timeout = 0;
+	     (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE);
+	     timeout++) {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	}
+	if (timeout >= MAX_TIMEOUT) {
+		dev_err(adap, "Idle wait Timeout! STS=0x%02x\n", temp);
+	}
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI15X3_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = ALI15X3_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = ALI15X3_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = ALI15X3_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0) {
+				len = 0;
+				data->block[0] = len;
+			}
+			if (len > 32) {
+				len = 32;
+				data->block[0] = len;
+			}
+			outb_p(len, SMBHSTDAT0);
+			/* Reset SMBBLKDAT */
+			outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = ALI15X3_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-ali15x3.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p(size, SMBHSTCNT);	/* output command */
+
+	if (ali15x3_transaction(adap))	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case ALI15X3_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI15X3_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI15X3_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case ALI15X3_BLOCK_DATA:
+		len = inb_p(SMBHSTDAT0);
+		if (len > 32)
+			len = 32;
+		data->block[0] = len;
+		/* Reset SMBBLKDAT */
+		outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+			dev_dbg(adap, "Blk: len=%d, i=%d, data=%02x\n",
+				len, i, data->block[i]);
+		}
+		break;
+	}
+	return 0;
+}
+
+static void ali15x3_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void ali15x3_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static u32 ali15x3_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= ali15x3_access,
+	.functionality	= ali15x3_func,
+};
+
+static struct i2c_adapter ali15x3_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3,
+	.algo		= &smbus_algorithm,
+	.inc_use	= ali15x3_inc,
+	.dec_use	= ali15x3_dec,
+};
+
+static struct pci_device_id ali15x3_ids[] __devinitdata = {
+	{
+	.vendor =	PCI_VENDOR_ID_AL,
+	.device =	PCI_DEVICE_ID_AL_M7101,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (ali15x3_setup(dev)) {
+		dev_err(dev,
+			"ALI15X3 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	snprintf(ali15x3_adapter.name, 32,
+		"SMBus ALI15X3 adapter at %04x", ali15x3_smba);
+	return i2c_add_adapter(&ali15x3_adapter);
+}
+
+static void __devexit ali15x3_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&ali15x3_adapter);
+	release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
+}
+
+static struct pci_driver ali15x3_driver = {
+	.name		= "ali15x3 smbus",
+	.id_table	= ali15x3_ids,
+	.probe		= ali15x3_probe,
+	.remove		= __devexit_p(ali15x3_remove),
+};
+
+static int __init i2c_ali15x3_init(void)
+{
+	printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&ali15x3_driver);
+}
+
+static void __exit i2c_ali15x3_exit(void)
+{
+	pci_unregister_driver(&ali15x3_driver);
+}
+
+MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
+		"Philip Edelbrock <phil@netroedge.com>, "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("ALI15X3 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ali15x3_init);
+module_exit(i2c_ali15x3_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis630.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis630.c	(revision 3149)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis630.c	(revision 3149)
@@ -0,0 +1,544 @@
+/*
+    i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
+
+    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.
+*/
+
+/*
+   Changes:
+   24.08.2002
+   	Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
+	Changed sis630_transaction.(Thanks to Mark M. Hoffman)
+   18.09.2002
+	Added SIS730 as supported.
+   21.09.2002
+	Added high_clock module option.If this option is set
+	used Host Master Clock 56KHz (default 14KHz).For now we save old Host
+	Master Clock and after transaction completed restore (otherwise
+	it's confuse BIOS and hung Machine).
+   24.09.2002
+	Fixed typo in sis630_access
+	Fixed logical error by restoring of Host Master Clock
+   31.07.2003
+   	Added block data read/write support.
+*/
+
+/*
+   Status: beta
+
+   Supports:
+	SIS 630
+	SIS 730
+
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+
+#ifdef DEBUG
+#define DBG(x...) printk(KERN_DEBUG "i2c-sis630.o: " x)
+#else
+#define DBG(x...)
+#endif
+
+/* SIS630 SMBus registers */
+#define SMB_STS     0x80 /* status */
+#define SMB_EN      0x81 /* status enable */
+#define SMB_CNT     0x82
+#define SMBHOST_CNT 0x83
+#define SMB_ADDR    0x84
+#define SMB_CMD     0x85
+#define SMB_PCOUNT  0x86 /* processed count */
+#define SMB_COUNT   0x87
+#define SMB_BYTE    0x88 /* ~0x8F data byte field */
+#define SMBDEV_ADDR 0x90
+#define SMB_DB0     0x91
+#define SMB_DB1     0x92
+#define SMB_SAA     0x93
+
+/* register count for request_region */
+#define SIS630_SMB_IOREGION 20
+
+/* PCI address constants */
+/* acpi base address register  */
+#define SIS630_ACPI_BASE_REG 0x74
+/* bios control register */
+#define SIS630_BIOS_CTL_REG  0x40
+
+/* Other settings */
+#define MAX_TIMEOUT   500
+
+/* SIS630 constants */
+#define SIS630_QUICK      0x00
+#define SIS630_BYTE       0x01
+#define SIS630_BYTE_DATA  0x02
+#define SIS630_WORD_DATA  0x03
+#define SIS630_PCALL      0x04
+#define SIS630_BLOCK_DATA 0x05
+
+static struct pci_driver sis630_driver;
+
+/* insmod parameters */
+static int high_clock = 0;
+static int force = 0;
+MODULE_PARM(high_clock, "i");
+MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
+
+/* acpi base address */
+static unsigned short acpi_base = 0;
+
+/* supported chips */
+static int supported[] = {
+	PCI_DEVICE_ID_SI_630,
+	PCI_DEVICE_ID_SI_730,
+	0 /* terminates the list */
+};
+
+static inline u8 sis630_read(u8 reg) {
+	return inb(acpi_base + reg);
+}
+
+static inline void sis630_write(u8 reg, u8 data) {
+	outb(data, acpi_base + reg);
+}
+
+static int sis630_transaction_start(int size, u8 *oldclock) {
+        int temp;
+
+        /*
+	  Make sure the SMBus host is ready to start transmitting.
+	*/
+	if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
+                DBG("SMBus busy (%02x).Resetting...\n",temp);
+		/* kill smbus transaction */
+		sis630_write(SMBHOST_CNT, 0x20);
+
+		if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
+                        DBG("Failed! (%02x)\n", temp);
+			return -1;
+                } else {
+                        DBG("Successfull!\n");
+		}
+        }
+
+	/* save old clock, so we can prevent machine for hung */
+	*oldclock = sis630_read(SMB_CNT);
+
+	DBG("saved clock 0x%02x\n", *oldclock);
+
+	/* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
+	if (high_clock > 0)
+		sis630_write(SMB_CNT, 0x20);
+	else
+		sis630_write(SMB_CNT, (*oldclock & ~0x40));
+
+	/* clear all sticky bits */
+	temp = sis630_read(SMB_STS);
+	sis630_write(SMB_STS, temp & 0x1e);
+
+	/* start the transaction by setting bit 4 and size */
+	sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
+
+	return 0;
+}
+
+static int sis630_transaction_wait(int size) {
+        int temp, result = 0, timeout = 0;
+
+        /* We will always wait for a fraction of a second! */
+        do {
+                i2c_delay(1);
+                temp = sis630_read(SMB_STS);
+		/* check if block transmitted */
+		if (size == SIS630_BLOCK_DATA && (temp & 0x10))
+		    break;
+        } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
+
+        /* If the SMBus is still busy, we give up */
+        if (timeout >= MAX_TIMEOUT) {
+                DBG("SMBus Timeout!\n");
+		result = -1;
+        }
+
+        if (temp & 0x02) {
+                result = -1;
+                DBG("Error: Failed bus transaction\n");
+	}
+
+        if (temp & 0x04) {
+                result = -1;
+                printk(KERN_ERR "i2c-sis630.o: Bus collision!\n");
+		/*
+		   TBD: Datasheet say:
+		   	the software should clear this bit and restart SMBUS operation.
+			Should we do it or user start request again?
+		*/
+        }
+
+	return result;
+}
+
+static void sis630_transaction_end(u8 oldclock) {
+        int temp = 0;
+
+        /* clear all status "sticky" bits */
+	sis630_write(SMB_STS, temp);
+
+	DBG("SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
+
+	/*
+	* restore old Host Master Clock if high_clock is set
+	* and oldclock was not 56KHz
+	*/
+	if (high_clock > 0 && !(oldclock & 0x20))
+		sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
+
+	DBG("SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
+}
+
+static int sis630_transaction(int size) {
+        int result = 0;
+	u8 oldclock = 0;
+
+	if (!(result = sis630_transaction_start(size, &oldclock))) {
+		result = sis630_transaction_wait(size);
+		sis630_transaction_end(oldclock);
+	}
+
+        return result;
+}
+
+static int sis630_block_data(union i2c_smbus_data * data, int read_write) {
+	int i, len = 0, rc = 0;
+	u8 oldclock = 0;
+
+	if (read_write == I2C_SMBUS_WRITE) {
+		len = data->block[0];
+		if (len < 0)
+			len = 0;
+		else if (len > 32)
+			len = 32;
+		sis630_write(SMB_COUNT, len);
+		for (i=1; i <= len; i++) {
+			DBG("set data 0x%02x\n", data->block[i]);
+			/* set data */
+			sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
+			if (i==8 || (len<8 && i==len)) {
+				DBG("start trans len=%d i=%d\n",len ,i);
+				/* first transaction */
+				if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock))
+					return -1;
+			}
+			else if ((i-1)%8 == 7 || i==len) {
+				DBG("trans_wait len=%d i=%d\n",len,i);
+				if (i>8) {
+					DBG("clear smbary_sts len=%d i=%d\n",len,i);
+					/*
+					   If this is not first transaction,
+					   we must clear sticky bit.
+					   clear SMBARY_STS
+					*/
+					sis630_write(SMB_STS,0x10);
+				}
+				if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
+					DBG("trans_wait failed\n");
+					rc = -1;
+					break;
+				}
+
+			}
+		}
+	}
+	else { /* read request */
+		data->block[0] = len = 0;
+		if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) {
+			return -1;
+		}
+		do {
+			if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
+				DBG("trans_wait failed\n");
+				rc = -1;
+				break;
+			}
+			/* if this first transaction then read byte count */
+			if (len == 0)
+				data->block[0] = sis630_read(SMB_COUNT);
+
+			/* just to be sure */
+			if (data->block[0] > 32)
+				data->block[0] = 32;
+
+			DBG("block data read len=0x%x\n", data->block[0]);
+
+			for (i=0; i < 8 && len < data->block[0]; i++,len++) {
+				DBG("read i=%d len=%d\n", i, len);
+				data->block[len+1] = sis630_read(SMB_BYTE+i);
+			}
+
+			DBG("clear smbary_sts len=%d i=%d\n",len,i);
+
+			/* clear SMBARY_STS */
+			sis630_write(SMB_STS,0x10);
+		} while(len < data->block[0]);
+	}
+
+	sis630_transaction_end(oldclock);
+
+	return rc;
+}
+
+/* Return -1 on error. */
+static s32 sis630_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+
+	switch (size) {
+		case I2C_SMBUS_QUICK:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			size = SIS630_QUICK;
+			break;
+		case I2C_SMBUS_BYTE:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			if (read_write == I2C_SMBUS_WRITE)
+				sis630_write(SMB_CMD, command);
+			size = SIS630_BYTE;
+			break;
+		case I2C_SMBUS_BYTE_DATA:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE)
+				sis630_write(SMB_BYTE, data->byte);
+			size = SIS630_BYTE_DATA;
+			break;
+		case I2C_SMBUS_PROC_CALL:
+		case I2C_SMBUS_WORD_DATA:
+			sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				sis630_write(SMB_BYTE, data->word & 0xff);
+				sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
+			}
+			size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
+			break;
+		case I2C_SMBUS_BLOCK_DATA:
+			sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			size = SIS630_BLOCK_DATA;
+			return sis630_block_data(data, read_write);
+		default:
+			printk("Unsupported I2C size\n");
+			return -1;
+			break;
+	}
+
+
+	if (sis630_transaction(size))
+		return -1;
+
+        if ((size != SIS630_PCALL) &&
+		((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
+                return 0;
+	}
+
+	switch(size) {
+		case SIS630_BYTE:
+		case SIS630_BYTE_DATA:
+			data->byte = sis630_read(SMB_BYTE);
+			break;
+		case SIS630_PCALL:
+		case SIS630_WORD_DATA:
+			data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
+			break;
+		default:
+			return -1;
+			break;
+	}
+
+	return 0;
+}
+
+static void sis630_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void sis630_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static u32 sis630_func(struct i2c_adapter *adapter) {
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+		I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static int __devinit sis630_setup(struct pci_dev *sis630_dev) {
+	unsigned char b;
+	struct pci_dev *dummy = NULL;
+	int i;
+
+	/* check for supported SiS devices */
+	for (i=0; supported[i] > 0; i++) {
+		if ((dummy = pci_find_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
+			break; /* found */
+	}
+
+	if (!dummy && force > 0) {
+		printk(KERN_ERR "i2c-sis630.o: WARNING: Can't detect SIS630 compatible device, but "
+			"loading because of force option enabled\n");
+	}
+	else if (!dummy) {
+		return -ENODEV;
+	}
+
+	/*
+	   Enable ACPI first , so we can accsess reg 74-75
+	   in acpi io space and read acpi base addr
+	*/
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't read bios ctl reg\n");
+		return -ENODEV;
+	}
+
+	/* if ACPI already enabled , do nothing */
+	if (!(b & 0x80) &&
+	    PCIBIOS_SUCCESSFUL !=
+	    pci_write_config_byte(sis630_dev,SIS630_BIOS_CTL_REG,b|0x80)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't enable ACPI\n");
+		return -ENODEV;
+	}
+	/* Determine the ACPI base address */
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't determine ACPI base address\n");
+		return -ENODEV;
+	}
+
+	DBG("ACPI base at 0x%04x\n", acpi_base);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
+			    sis630_driver.name)){
+		printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x "
+			"already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA);
+		acpi_base = 0; /* reset acpi_base */
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis630_access,
+	.functionality	= sis630_func,
+};
+
+static struct i2c_adapter sis630_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS630,
+	.algo		= &smbus_algorithm,
+	.inc_use	= sis630_inc,
+	.dec_use	= sis630_dec,
+};
+
+
+static struct pci_device_id sis630_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_SI,
+		.device =	PCI_DEVICE_ID_SI_503,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (sis630_setup(dev)) {
+		printk(KERN_ERR "i2c-sis630.o: SIS630 comp. bus not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
+		acpi_base + SMB_STS);
+
+	return i2c_add_adapter(&sis630_adapter);
+}
+
+static void __devexit sis630_remove(struct pci_dev *dev)
+{
+	if (acpi_base) {
+		i2c_del_adapter(&sis630_adapter);
+		release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
+		acpi_base = 0;
+	}
+}
+
+
+static struct pci_driver sis630_driver = {
+	.name		= "sis630 smbus",
+	.id_table	= sis630_ids,
+	.probe		= sis630_probe,
+	.remove		= __devexit_p(sis630_remove),
+};
+
+static int __init i2c_sis630_init(void)
+{
+	printk("i2c-sis630.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&sis630_driver);
+}
+
+
+static void __exit i2c_sis630_exit(void)
+{
+	pci_unregister_driver(&sis630_driver);
+}
+
+
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
+MODULE_DESCRIPTION("SIS630 SMBus driver");
+
+module_init(i2c_sis630_init);
+module_exit(i2c_sis630_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmb.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmb.c	(revision 2987)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmb.c	(revision 2987)
@@ -0,0 +1,380 @@
+/*
+    i2c-ipmb.c - Part of lm_sensors, Linux kernel modules for hardware
+            monitoring
+    Copyright (c) 2002 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 DOESN'T WORK YET - DON'T BOTHER TRYING IT.	
+    This implements an i2c adapter for the BMC IPMB.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/ipmi.h>
+#include "version.h"
+
+
+static u32 i2c_ipmb_func(struct i2c_adapter *adapter);
+int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[], 
+	                   int num);
+
+
+/* I2C Data */
+static struct i2c_algorithm i2c_ipmb_algorithm = {
+	.name = "IPMB algorithm",
+	.id = I2C_ALGO_IPMB,
+	.master_xfer = ipmb_access,
+	.functionality = i2c_ipmb_func,
+};
+
+#define MAX_IPMB_ADAPTERS 8
+static struct i2c_adapter i2c_ipmb_adapter[MAX_IPMB_ADAPTERS];
+
+/* IPMI Data */
+#define IPMI_IPMB_CHANNEL	0
+static ipmi_user_t i2c_ipmb_user;
+static unsigned char ipmi_version_major;
+static unsigned char ipmi_version_minor;
+static const char msgdata[IPMI_MAX_ADDR_SIZE];   /* ?? */
+static struct ipmi_system_interface_addr address = {
+	IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
+	IPMI_BMC_CHANNEL,
+	0
+};	/* send address */
+static struct ipmi_ipmb_addr ipmb_address = {
+	IPMI_IPMB_ADDR_TYPE,
+	IPMI_IPMB_CHANNEL,
+	0,
+	0
+};	/* send address */
+static long msgid;		/* message ID */
+static int interfaces;		/* number of BMC's found */
+static struct ipmi_msg tx_message;	/* send message */
+static unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
+static unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50]; /* sloppy */
+
+/* IPMI Message defs */
+/* Network Function Codes */
+#define IPMI_NETFN_APP		0x06
+/* Commands */
+#define IPMI_ENABLE_CHANNEL	0x32
+#define IPMI_SEND_MSG		0x34
+#define IPMI_GET_CHANNEL_INFO	0x42	/* unfortunately, IPMI 1.5 only */
+#define IPMI_MASTER_WR		0x52
+
+
+/************** Message Sending **************/
+
+static void ipmb_i2c_send_message(struct ipmi_addr *address,
+                                  int id, struct ipmi_msg * msg)
+{
+	int err;
+
+#ifdef IPMI_RESPONSE_RESPONSE_TYPE
+	err = ipmi_request(i2c_ipmb_user, address, id, msg, NULL, 0);
+#else
+	err = ipmi_request(i2c_ipmb_user, address, id, msg, 0);
+#endif
+	if (err)
+		printk(KERN_INFO "i2c-ipmb.o: ipmi_request error %d\n",
+			err);
+}
+
+/* not used */
+static void ipmb_i2c_bmc_send_message(int id, struct ipmi_msg * msg)
+{
+	address.channel = IPMI_BMC_CHANNEL;
+	ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg);
+}
+
+/* this is for sending commands like master w/r */
+static void ipmb_i2c_ipmb_send_message(int id, struct ipmi_msg * msg)
+{
+/*
+	address.channel = IPMI_IPMB_CHANNEL;
+*/
+	address.channel = IPMI_BMC_CHANNEL;
+	ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg);
+}
+
+#if 0
+/* not used */
+/* this is for an smi message, not for things like master w/r */
+static void ipmb_i2c_ipmb_send_smi(int addr, int id, struct ipmi_msg * msg)
+{
+	ipmb_address.slave_addr = addr;
+	ipmb_address.lun = 0;  /* pass in */
+	ipmb_i2c_send_message((struct ipmi_addr *) &ipmb_address, id, msg);
+}
+
+/* not used */
+/* Compose and send a "Enable Channel Receive" message */
+static void ipmb_enable_channel_rcv(int channel, int code)
+{
+	tx_message.netfn = IPMI_NETFN_APP;
+	tx_message.cmd = IPMI_ENABLE_CHANNEL;	
+	tx_message.data_len = 2;
+	tx_message.data = tx_msg_data;
+	tx_msg_data[0] = channel;
+	tx_msg_data[1] = code;
+	ipmb_i2c_bmc_send_message(msgid++, &tx_message);
+}
+
+/* not used */
+/* Compose and send a "Get Channel Info" message (1.5 only) */
+static void ipmb_get_channel_info(int channel)
+{
+	tx_message.netfn = IPMI_NETFN_APP;
+	tx_message.cmd = IPMI_GET_CHANNEL_INFO;	
+	tx_message.data_len = 1;
+	tx_message.data = tx_msg_data;
+	tx_msg_data[0] = channel;
+	ipmb_i2c_bmc_send_message(msgid++, &tx_message);
+}
+#endif /* 0 */
+
+/* Compose and send a "Master W/R" message */
+static void ipmb_master_wr(int bus, u8 addr, u8 rdcount,
+                           u8 wrcount, u8 *wrdata)
+{
+	printk(KERN_INFO "i2c-ipmb.o: trying bus %d ...\n", bus);
+	tx_message.netfn = IPMI_NETFN_APP;
+	tx_message.cmd = IPMI_MASTER_WR;	
+	tx_message.data_len = 3 + wrcount;
+	tx_msg_data[0] = bus & 0x0f;
+	tx_msg_data[1] = addr << 1;
+	tx_msg_data[2] = rdcount;
+	if(wrcount > 0)
+		memcpy(tx_msg_data + 3, wrdata, wrcount);
+	tx_message.data = tx_msg_data;
+	ipmb_i2c_ipmb_send_message(msgid++, &tx_message);
+}
+
+int xchan;
+int xbus;
+/* look for channels. IPMI 1.5 defines multiple channels and an
+   easy way to get the information.
+   We don't bother using SDR type 14 for IPMI 1.0; that isn't
+   always present anyway. We could try "enable message channel receive"
+   with a channel state = 2 (query) but 1.0 is likely to have only
+   IPMB busses anyway. Therefore, we assume there is only
+   the IPMB at channel 0.
+*/
+static void ipmb_get_all_channel_info(void)
+
+{
+#if 0
+	if(ipmi_version_major > 1 ||
+	   (ipmi_version_major == 1 && ipmi_version_minor >= 5))
+		ipmb_get_channel_info(0);
+#endif
+/*
+	else
+		scan SDR's for type 14
+	else {
+		for all xchan 0-15
+		ipmb_enable_channel_rcv(xchan, 2);
+*/
+	xbus = 0;
+		ipmb_master_wr(xbus, 0x2d, 1, 1, "\0");
+/*
+*/
+/*
+	else
+		assume IPMB at channel 0 only
+*/
+}
+
+/************** Message Receiving **************/
+
+/* not used */
+static void ipmb_rcv_channel_info(struct ipmi_msg *msg)
+{
+	u8 channel, type, protocol;
+
+	channel = msg->data[1] & 0x0f;
+	type = msg->data[2] & 0x7f;
+	protocol = msg->data[3] & 0x1f;
+	printk(KERN_INFO "i2c-ipmb.o: Channel %d; type 0x%x; protocol 0x%x\n",
+	                  channel, type, protocol);
+}
+/*
+	return i2c_add_adapter(&i2c_ipmb_adapter);
+	if (error) {
+		printk(KERN_ERR "i2c-ipmb.o: Adapter registration failed, "
+		       "module i2c-ipmb.o is not inserted\n.");
+		return;
+	}
+*/
+/*
+	if(channel < 7)
+		ipmb_get_channel_info(channel + 1);
+*/
+static void ipmb_rcv_master_resp(struct ipmi_msg *msg)
+{
+	if(++xbus > 0x0f)
+		return;
+	ipmb_master_wr(xbus, 0x2d, 1, 1, "\0");
+}
+
+#if 0
+/* not used */
+static void ipmb_rcv_channel_enable(struct ipmi_msg *msg)
+{
+	int state;
+
+	state = msg->data[2] & 1;
+
+	printk(KERN_INFO "i2c-ipmb.o: Channel %d; state %d\n",
+	                  xchan, state);
+		if(++xchan > 0x0f)
+			return;
+	ipmb_enable_channel_rcv(xchan, 2);
+}
+#endif /* 0 */
+
+static void ipmb_i2c_msg_handler(struct ipmi_recv_msg *msg,
+				  void            *handler_data)
+{
+	int rcvid = msg->msgid & 0xffffff;
+	int client = (msg->msgid >> 24) & 0xf;
+
+	if (msg->msg.data[0] != 0)
+		printk(KERN_WARNING "i2c-ipmb.o: Error 0x%x on cmd 0x%x/0x%x\n",
+		       msg->msg.data[0], msg->msg.netfn & 0xfe, msg->msg.cmd);
+/*
+	else
+*/
+		ipmb_rcv_master_resp(&(msg->msg));
+
+
+
+
+
+	ipmi_free_recv_msg(msg);
+}
+
+static struct ipmi_user_hndl ipmb_hndlrs =
+{
+	.ipmi_recv_hndl           = ipmb_i2c_msg_handler,
+};
+
+/*************** I2C funtions *******************/
+
+/* Return -1 on error. */
+int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[], 
+	                   int num)
+{
+
+
+
+
+
+
+}
+
+static u32 i2c_ipmb_func(struct i2c_adapter *adapter)
+{
+	return 0; /* fixme */
+}
+
+/**************** Initialization ****************/
+
+/* callback for each BMC found */
+static void ipmb_register(int ipmi_intf)
+{
+	unsigned long flags;
+	int rv;
+
+	if(interfaces > 0) {	/* 1 max for now */
+		printk(KERN_INFO
+		       "i2c-ipmb.o: Additional IPMI interface %d not supported\n",
+		       ipmi_intf);
+		return;
+	}
+
+	rv = ipmi_create_user(ipmi_intf, &ipmb_hndlrs, NULL, &i2c_ipmb_user);
+	if (rv < 0) {
+		printk(KERN_ERR "i2c-ipmb.o: Unable to register with ipmi\n");
+		return;
+	}
+
+	ipmi_get_version(i2c_ipmb_user, &ipmi_version_major,
+	                 &ipmi_version_minor);
+	printk(KERN_INFO
+	       "i2c-ipmb.o: Registered IPMI interface %d with version %d.%d\n",
+	       ipmi_intf, ipmi_version_major, ipmi_version_minor);
+	interfaces++;
+
+	ipmb_get_all_channel_info();
+}
+
+static void ipmb_new_smi(int if_num)
+{
+	ipmb_register(if_num);
+}
+
+static void ipmb_smi_gone(int if_num)
+{
+	if (interfaces >= 1) {
+/*
+		i2c_del_adapter(&i2c_ipmb_adapter);
+*/
+		ipmi_destroy_user(i2c_ipmb_user);
+		interfaces--;
+	}
+}
+
+static struct ipmi_smi_watcher smi_watcher =
+{
+	.new_smi  = ipmb_new_smi,
+	.smi_gone = ipmb_smi_gone
+};
+
+static int __init i2c_ipmb_init(void)
+{
+	int rv;
+
+	printk(KERN_INFO "i2c-ipmb.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	rv = ipmi_smi_watcher_register(&smi_watcher);
+	if (rv) {
+		printk(KERN_WARNING
+		       "ipmi_watchdog: can't register smi watcher\n");
+		return rv;
+	}
+
+	printk(KERN_INFO "i2c-ipmb.o: BMC access for i2c modules initialized.\n");
+	return 0;
+}
+
+
+static void __exit i2c_ipmi_exit(void)
+{
+	ipmi_smi_watcher_unregister(&smi_watcher);
+	ipmb_smi_gone(0);
+}
+
+MODULE_AUTHOR("M. D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("IPMB-BMC access through i2c");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ipmb_init);
+module_exit(i2c_ipmi_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-voodoo3.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-voodoo3.c	(revision 2802)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-voodoo3.c	(revision 2802)
@@ -0,0 +1,296 @@
+/*
+    voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    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 interfaces to the I2C bus of the Voodoo3 to gain access to
+    the BT869 and possibly other I2C devices. */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#include "version.h"
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+
+/* the only registers we use */
+#define REG	0x78
+#define REG2 	0x70
+
+/* bit locations in the register */
+#define DDC_ENAB	0x00040000
+#define DDC_SCL_OUT	0x00080000
+#define DDC_SDA_OUT	0x00100000
+#define DDC_SCL_IN	0x00200000
+#define DDC_SDA_IN	0x00400000
+#define I2C_ENAB	0x00800000
+#define I2C_SCL_OUT	0x01000000
+#define I2C_SDA_OUT	0x02000000
+#define I2C_SCL_IN	0x04000000
+#define I2C_SDA_IN	0x08000000
+
+/* initialization states */
+#define INIT2	0x2
+#define INIT3	0x4
+
+/* delays */
+#define CYCLE_DELAY	10
+#define TIMEOUT		(HZ / 2)
+
+
+static void config_v3(struct pci_dev *dev);
+
+static unsigned long ioaddr;
+
+/* The voo GPIO registers don't have individual masks for each bit
+   so we always have to read before writing. */
+
+static void bit_vooi2c_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SCL_OUT;
+	else
+		r &= ~I2C_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_vooi2c_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SDA_OUT;
+	else
+		r &= ~I2C_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_vooi2c_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
+}
+
+static int bit_vooi2c_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
+}
+
+static void bit_vooddc_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= DDC_SCL_OUT;
+	else
+		r &= ~DDC_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_vooddc_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= DDC_SDA_OUT;
+	else
+		r &= ~DDC_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static int bit_vooddc_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
+}
+
+static int bit_vooddc_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
+}
+
+
+/* Configures the chip */
+
+void config_v3(struct pci_dev *dev)
+{
+	unsigned int cadr;
+
+	/* map Voodoo3 memory */
+	cadr = dev->resource[0].start;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
+	if(ioaddr) {
+		writel(0x8160, ioaddr + REG2);
+		writel(0xcffc0020, ioaddr + REG);
+		printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%lx\n", ioaddr);
+	}
+}
+
+static void voodoo3_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void voodoo3_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_bit_data voo_i2c_bit_data = {
+	.setsda		= bit_vooi2c_setsda,
+	.setscl		= bit_vooi2c_setscl,
+	.getsda		= bit_vooi2c_getsda,
+	.getscl		= bit_vooi2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_i2c_adapter = {
+	.name		= "I2C Voodoo3/Banshee adapter",
+	.id		= I2C_HW_B_VOO,
+	.algo_data	= &voo_i2c_bit_data,
+	.inc_use	= voodoo3_inc,
+	.dec_use	= voodoo3_dec,
+};
+
+static struct i2c_algo_bit_data voo_ddc_bit_data = {
+	.setsda		= bit_vooddc_setsda,
+	.setscl		= bit_vooddc_setscl,
+	.getsda		= bit_vooddc_getsda,
+	.getscl		= bit_vooddc_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_ddc_adapter = {
+	.name		= "DDC Voodoo3/Banshee adapter",
+	.id		= I2C_HW_B_VOO,
+	.algo_data	= &voo_ddc_bit_data,
+	.inc_use	= voodoo3_inc,
+	.dec_use	= voodoo3_dec,
+};
+
+
+static struct pci_device_id voodoo3_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_3DFX,
+		.device =	PCI_DEVICE_ID_3DFX_VOODOO3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_3DFX,
+		.device =	PCI_DEVICE_ID_3DFX_BANSHEE,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int retval;
+
+	config_v3(dev);
+	retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
+	if(retval)
+		return retval;
+	retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
+	if(retval)
+		i2c_bit_del_bus(&voodoo3_i2c_adapter);
+	return retval;
+}
+
+static void __devexit voodoo3_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&voodoo3_i2c_adapter);
+ 	i2c_bit_del_bus(&voodoo3_ddc_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver voodoo3_driver = {
+	.name		= "voodoo3 smbus",
+	.id_table	= voodoo3_ids,
+	.probe		= voodoo3_probe,
+	.remove		= __devexit_p(voodoo3_remove),
+};
+*/
+
+static int __init i2c_voodoo3_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&voodoo3_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(voodoo3_ids, dev);
+		if(id)
+			if(voodoo3_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_voodoo3_exit(void)
+{
+/*
+	pci_unregister_driver(&voodoo3_driver);
+*/
+	voodoo3_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
+
+module_init(i2c_voodoo3_init);
+module_exit(i2c_voodoo3_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756-s4882.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756-s4882.c	(revision 3292)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756-s4882.c	(revision 3292)
@@ -0,0 +1,264 @@
+/*
+ * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
+ *
+ * Copyright (C) 2004 Jean Delvare <khali@linux-fr.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.
+ */
+ 
+/*
+ * We select the channels by sending commands to the Philips
+ * PCA9556 chip at I2C address 0x18. The main adapter is used for
+ * the non-multiplexed part of the bus, and 4 virtual adapters
+ * are defined for the multiplexed addresses: 0x50-0x53 (memory
+ * module EEPROM) located on channels 1-4, and 0x4c (LM63)
+ * located on multiplexed channels 0 and 5-7. We define one
+ * virtual adapter per CPU, which corresponds to two multiplexed
+ * channels:
+ *   CPU0: virtual adapter 1, channels 1 and 0
+ *   CPU1: virtual adapter 2, channels 2 and 5
+ *   CPU2: virtual adapter 3, channels 3 and 6
+ *   CPU3: virtual adapter 4, channels 4 and 7
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <asm/semaphore.h>
+
+#define DRV_NAME	"i2c-amd756-s4882"
+
+extern struct i2c_adapter amd756_smbus;
+
+static struct i2c_adapter *s4882_adapter;
+static struct i2c_algorithm *s4882_algo;
+
+/* Wrapper access functions for multiplexed SMBus */
+static DECLARE_MUTEX(amd756_lock);
+
+static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data * data)
+{
+	int error;
+
+	/* We exclude the multiplexed addresses */
+	if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
+	 || addr == 0x18)
+		return -1;
+
+	down(&amd756_lock);
+
+	error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
+					      command, size, data);
+
+	up(&amd756_lock);
+
+	return error;
+}
+
+/* We remember the last used channels combination so as to only switch
+   channels when it is really needed. This greatly reduces the SMBus
+   overhead, but also assumes that nobody will be writing to the PCA9556
+   in our back. */
+static u8 last_channels;
+
+static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
+					unsigned short flags, char read_write,
+					u8 command, int size,
+					union i2c_smbus_data * data,
+					u8 channels)
+{
+	int error;
+
+	/* We exclude the non-multiplexed addresses */
+	if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
+		return -1;
+
+	down(&amd756_lock);
+
+	if (last_channels != channels) {
+		union i2c_smbus_data mplxdata;
+		mplxdata.byte = channels;
+
+		error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
+						      I2C_SMBUS_WRITE, 0x01,
+						      I2C_SMBUS_BYTE_DATA,
+						      &mplxdata);
+		if (error)
+			goto UNLOCK;
+		last_channels = channels;
+	}
+	error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
+					      command, size, data);
+
+UNLOCK:
+	up(&amd756_lock);
+	return error;
+}
+
+static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data * data)
+{
+	/* CPU0: channels 1 and 0 enabled */
+	return amd756_access_channel(adap, addr, flags, read_write, command,
+				     size, data, 0x03);
+}
+
+static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data * data)
+{
+	/* CPU1: channels 2 and 5 enabled */
+	return amd756_access_channel(adap, addr, flags, read_write, command,
+				     size, data, 0x24);
+}
+
+static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data * data)
+{
+	/* CPU2: channels 3 and 6 enabled */
+	return amd756_access_channel(adap, addr, flags, read_write, command,
+				     size, data, 0x48);
+}
+
+static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data * data)
+{
+	/* CPU3: channels 4 and 7 enabled */
+	return amd756_access_channel(adap, addr, flags, read_write, command,
+				     size, data, 0x90);
+}
+
+static int __init amd756_s4882_init(void)
+{
+	int i, error;
+	union i2c_smbus_data ioconfig;
+
+	/* Unregister physical bus */
+	error = i2c_del_adapter(&amd756_smbus);
+	if (error) {
+		if (error != -ENODEV)
+			printk(KERN_ERR DRV_NAME ": Physical bus removal "
+			       "failed\n");
+		goto ERROR0;
+	}
+
+	printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
+
+	/* Define the 5 virtual adapters and algorithms structures */
+	if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
+				      GFP_KERNEL))) {
+		error = -ENOMEM;
+		goto ERROR1;
+	}
+	if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
+				   GFP_KERNEL))) {
+		error = -ENOMEM;
+		goto ERROR2;
+	}
+
+	/* Fill in the new structures */
+	s4882_algo[0] = *(amd756_smbus.algo);
+	s4882_algo[0].smbus_xfer = amd756_access_virt0;
+	s4882_adapter[0] = amd756_smbus;
+	s4882_adapter[0].algo = s4882_algo;
+	for (i = 1; i < 5; i++) {
+		s4882_algo[i] = *(amd756_smbus.algo);
+		s4882_adapter[i] = amd756_smbus;
+		sprintf(s4882_adapter[i].name,
+			"SMBus 8111 adapter (CPU%d)", i-1);
+		s4882_adapter[i].algo = s4882_algo+i;
+	}
+	s4882_algo[1].smbus_xfer = amd756_access_virt1;
+	s4882_algo[2].smbus_xfer = amd756_access_virt2;
+	s4882_algo[3].smbus_xfer = amd756_access_virt3;
+	s4882_algo[4].smbus_xfer = amd756_access_virt4;
+
+	/* Configure the PCA9556 multiplexer */
+	ioconfig.byte = 0x00; /* All I/O to output mode */
+	error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
+					      I2C_SMBUS_WRITE, 0x03,
+					      I2C_SMBUS_BYTE_DATA, &ioconfig);
+	if (error) {
+		printk(KERN_ERR DRV_NAME ": PCA9556 configuration failed\n");
+		error = -EIO;
+		goto ERROR3;
+	}
+
+	/* Register virtual adapters */
+	for (i = 0; i < 5; i++) {
+		error = i2c_add_adapter(s4882_adapter+i);
+		if (error) {
+			printk(KERN_ERR DRV_NAME
+			       ": Virtual adapter %d registration "
+			       "failed, module not inserted\n", i);
+			for (i--; i >= 0; i--)
+				i2c_del_adapter(s4882_adapter+i);
+			goto ERROR3;
+		}
+	}
+
+	return 0;
+
+ERROR3:
+	kfree(s4882_algo);
+	s4882_algo = NULL;
+ERROR2:
+	kfree(s4882_adapter);
+	s4882_adapter = NULL;
+ERROR1:
+	i2c_del_adapter(&amd756_smbus);
+ERROR0:
+	return error;
+}
+
+static void __exit amd756_s4882_exit(void)
+{
+	if (s4882_adapter) {
+		int i;
+
+		for (i = 0; i < 5; i++)
+			i2c_del_adapter(s4882_adapter+i);
+		kfree(s4882_adapter);
+		s4882_adapter = NULL;
+	}
+	if (s4882_algo) {
+		kfree(s4882_algo);
+		s4882_algo = NULL;
+	}
+
+	/* Restore physical bus */
+	if (i2c_add_adapter(&amd756_smbus))
+		printk(KERN_ERR DRV_NAME ": Physical bus restoration "
+		       "failed\n");
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("S4882 SMBus multiplexing");
+MODULE_LICENSE("GPL");
+
+module_init(amd756_s4882_init);
+module_exit(amd756_s4882_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-savage4.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-savage4.c	(revision 2802)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-savage4.c	(revision 2802)
@@ -0,0 +1,243 @@
+/*
+    i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (C) 1998-2003  The LM Sensors Team
+    Alexander Wold <awold@bigfoot.com>
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on i2c-voodoo3.c.
+
+    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 interfaces to the I2C bus of the Savage4 to gain access to
+   the BT869 and possibly other I2C devices. The DDC bus is not
+   yet supported because its register is not memory-mapped.
+   However we leave the DDC code here, commented out, to make
+   it easier to add later.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h> /* for HZ */
+#include "version.h"
+#include "sensors_compat.h"
+
+/* 3DFX defines */
+/* #define PCI_VENDOR_ID_S3		0x5333 */
+#define PCI_CHIP_SAVAGE3D	0x8A20
+#define PCI_CHIP_SAVAGE3D_MV	0x8A21
+#define PCI_CHIP_SAVAGE4	0x8A22
+#define PCI_CHIP_SAVAGE2000	0x9102
+#define PCI_CHIP_PROSAVAGE_PM	0x8A25
+#define PCI_CHIP_PROSAVAGE_KM	0x8A26
+#define PCI_CHIP_SAVAGE_MX_MV	0x8c10
+#define PCI_CHIP_SAVAGE_MX	0x8c11
+#define PCI_CHIP_SAVAGE_IX_MV	0x8c12
+#define PCI_CHIP_SAVAGE_IX	0x8c13
+
+#define REG 0xff20	/* Serial Port 1 Register */
+
+/* bit locations in the register */
+//#define DDC_ENAB	0x00040000
+//#define DDC_SCL_OUT	0x00080000
+//#define DDC_SDA_OUT	0x00100000
+//#define DDC_SCL_IN	0x00200000
+//#define DDC_SDA_IN	0x00400000
+#define I2C_ENAB	0x00000020
+#define I2C_SCL_OUT	0x00000001
+#define I2C_SDA_OUT	0x00000002
+#define I2C_SCL_IN	0x00000008
+#define I2C_SDA_IN	0x00000010
+
+/* initialization states */
+#define INIT2	0x20
+/* #define INIT3	0x4 */
+
+/* delays */
+#define CYCLE_DELAY	10
+#define TIMEOUT		(HZ / 2)
+
+
+static void config_s4(struct pci_dev *dev);
+
+static unsigned long ioaddr;
+
+/* The sav GPIO registers don't have individual masks for each bit
+   so we always have to read before writing. */
+
+static void bit_savi2c_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SCL_OUT;
+	else
+		r &= ~I2C_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_savi2c_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SDA_OUT;
+	else
+		r &= ~I2C_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_savi2c_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
+}
+
+static int bit_savi2c_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
+}
+
+/* Configures the chip */
+
+void config_s4(struct pci_dev *dev)
+{
+	unsigned int cadr;
+
+	/* map memory */
+	cadr = dev->resource[0].start;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x0080000);
+	if(ioaddr) {
+//		writel(0x8160, ioaddr + REG2);
+		writel(0x00000020, ioaddr + REG);
+		printk("i2c-savage4: Using Savage4 at 0x%lx\n", ioaddr);
+	}
+}
+
+static void savage4_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void savage4_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_bit_data sav_i2c_bit_data = {
+	.setsda		= bit_savi2c_setsda,
+	.setscl		= bit_savi2c_setscl,
+	.getsda		= bit_savi2c_getsda,
+	.getscl		= bit_savi2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter savage4_i2c_adapter = {
+	.name		= "I2C Savage4 adapter",
+	.id		= I2C_HW_B_SAVG,
+	.algo_data	= &sav_i2c_bit_data,
+	.inc_use	= savage4_inc,
+	.dec_use	= savage4_dec,
+};
+
+static struct pci_device_id savage4_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_S3,
+		.device =	PCI_CHIP_SAVAGE4,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_S3,
+		.device =	PCI_CHIP_SAVAGE2000,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	config_s4(dev);
+	return i2c_bit_add_bus(&savage4_i2c_adapter);
+}
+
+static void __devexit savage4_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&savage4_i2c_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver savage4_driver = {
+	.name		= "savage4 smbus",
+	.id_table	= savage4_ids,
+	.probe		= savage4_probe,
+	.remove		= __devexit_p(savage4_remove),
+};
+*/
+
+static int __init i2c_savage4_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-savage4.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&savage4_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(savage4_ids, dev);
+		if(id)
+			if(savage4_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit i2c_savage4_exit(void)
+{
+/*
+	pci_unregister_driver(&savage4_driver);
+*/
+	savage4_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_savage4_init);
+module_exit(i2c_savage4_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.c	(revision 3126)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.c	(revision 3126)
@@ -0,0 +1,619 @@
+/*
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+    Original work by
+    
+    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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.
+
+    Changes:
+
+    2001/12/13 BenH	New implementation
+    2001/12/15 BenH	Add support for "byte" and "quick"
+                        transfers. Add i2c_xfer routine.
+
+    My understanding of the various modes supported by keywest are:
+
+     - Dumb mode : not implemented, probably direct tweaking of lines
+     - Standard mode : simple i2c transaction of type
+         S Addr R/W A Data A Data ... T
+     - Standard sub mode : combined 8 bit subaddr write with data read
+         S Addr R/W A SubAddr A Data A Data ... T
+     - Combined mode : Subaddress and Data sequences appended with no stop
+         S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
+
+    Currently, this driver uses only Standard mode for i2c xfer, and
+    smbus byte & quick transfers ; and uses StandardSub mode for
+    other smbus transfers instead of combined as we need that for the
+    sound driver to be happy
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+
+#include "i2c-keywest.h"
+
+#define DBG(x...) do {\
+	if (debug > 0) \
+		printk(KERN_DEBUG "KW:" x); \
+	} while(0)
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
+
+int debug = 0;
+
+static struct keywest_iface *ifaces = NULL;
+
+
+static void
+do_stop(struct keywest_iface* iface, int result)
+{
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
+	iface->state = state_stop;
+	iface->result = result;
+}
+
+/* Main state machine for standard & standard sub mode */
+static int
+handle_interrupt(struct keywest_iface *iface, u8 isr)
+{
+	int ack;
+	int rearm_timer = 1;
+	
+	DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
+		isr, read_reg(reg_status), iface->state);
+	if (isr == 0 && iface->state != state_stop) {
+		do_stop(iface, -1);
+		return rearm_timer;
+	}
+	if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
+		iface->result = -1;
+		iface->state = state_stop;
+	}
+	switch(iface->state) {
+	case state_addr:
+		if (!(isr & KW_I2C_IRQ_ADDR)) {
+			do_stop(iface, -1);
+			break;
+		}
+		ack = read_reg(reg_status);
+		DBG("ack on set address: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Handle rw "quick" mode */
+		if (iface->datalen == 0)
+			do_stop(iface, 0);
+		else if (iface->read_write == I2C_SMBUS_READ) {
+			iface->state = state_read;
+			if (iface->datalen > 1)
+				write_reg(reg_control, read_reg(reg_control)
+					| KW_I2C_CTL_AAK);
+		} else {
+			iface->state = state_write;
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		}
+		
+		break;
+	case state_read:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		*(iface->data++) = read_reg(reg_data);
+		DBG("read byte: %x\n", *(iface->data-1));
+		iface->datalen--;
+		if (iface->datalen == 0)
+			iface->state = state_stop;
+		else
+			write_reg(reg_control, 0);
+		break;
+	case state_write:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Check ack status */
+		ack = read_reg(reg_status);
+		DBG("ack on data write: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		if (iface->datalen) {
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		} else
+			do_stop(iface, 0);
+		break;
+		
+	case state_stop:
+		if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
+			do_stop(iface, -1);
+		else {
+			rearm_timer = 0;
+			iface->state = state_idle;
+			write_reg(reg_control, 0x00);
+			write_reg(reg_ier, 0x00);
+			complete(&iface->complete);
+		}
+		break;
+	}
+	
+	write_reg(reg_isr, isr);
+
+	return rearm_timer;
+}
+
+/* Interrupt handler */
+static void
+keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)dev_id;
+
+	spin_lock(&iface->lock);
+	del_timer(&iface->timeout_timer);
+	if (handle_interrupt(iface, read_reg(reg_isr))) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+static void
+keywest_timeout(unsigned long data)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)data;
+
+	DBG("timeout !\n");
+	spin_lock_irq(&iface->lock);
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+/*
+ * SMBUS-type transfer entrypoint
+ */
+static s32
+keywest_smbus_xfer(	struct i2c_adapter*	adap,
+			u16			addr,
+			unsigned short		flags,
+			char			read_write,
+			u8			command,
+			int			size,
+			union i2c_smbus_data*	data)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	int len;
+	u8* buffer;
+	u16 cur_word;
+	int rc = 0;
+
+	if (iface->state == state_dead)
+		return -1;
+		
+	/* Prepare datas & select mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	switch (size) {
+	    case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	//iface->cur_mode |= KW_I2C_MODE_COMBINED;
+	    	break;
+	    case I2C_SMBUS_WORD_DATA:
+	    	len = 2;
+	    	cur_word = cpu_to_le16(data->word);
+	    	buffer = (u8 *)&cur_word;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	//iface->cur_mode |= KW_I2C_MODE_COMBINED;
+		break;
+	    case I2C_SMBUS_BLOCK_DATA:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	//iface->cur_mode |= KW_I2C_MODE_COMBINED;
+		break;
+	    default:
+	    	return -1;
+	}
+
+	/* Original driver had this limitation */
+	if (len > 32)
+		len = 32;
+
+	down(&iface->sem);
+
+	DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
+		chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
+
+	iface->data = buffer;
+	iface->datalen = len;
+	iface->state = state_addr;
+	iface->result = 0;
+	iface->stopretry = 0;
+	iface->read_write = read_write;
+	
+	/* Setup channel & clear pending irqs */
+	write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+	write_reg(reg_isr, read_reg(reg_isr));
+	write_reg(reg_status, 0);
+
+	/* Set up address and r/w bit */
+	write_reg(reg_addr,
+		(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+	/* Set up the sub address */
+	if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		write_reg(reg_subaddr, command);
+
+	/* Arm timeout */
+	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+	add_timer(&iface->timeout_timer);
+
+	/* Start sending address & enable interrupt*/
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+	write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+	wait_for_completion(&iface->complete);	
+
+	rc = iface->result;	
+	DBG("transfer done, result: %d\n", rc);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+	
+	/* Release sem */
+	up(&iface->sem);
+	
+	return rc;
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int
+keywest_xfer(	struct i2c_adapter *adap,
+		struct i2c_msg msgs[], 
+		int num)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	struct i2c_msg *pmsg;
+	int i, completed;
+	int rc = 0;
+
+	down(&iface->sem);
+
+	/* Set adapter to standard mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+
+	completed = 0;
+	for (i = 0; rc >= 0 && i < num;) {
+		u8 addr;
+		
+		pmsg = &msgs[i++];
+		addr = pmsg->addr;
+		if (pmsg->flags & I2C_M_TEN) {
+			printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
+			rc = -EINVAL;
+			break;
+		}
+		DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
+		     chan->chan_no,
+		     pmsg->flags & I2C_M_RD ? "read" : "write",
+                     pmsg->len, addr, i, num);
+    
+		/* Setup channel & clear pending irqs */
+		write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+		write_reg(reg_isr, read_reg(reg_isr));
+		write_reg(reg_status, 0);
+		
+		iface->data = pmsg->buf;
+		iface->datalen = pmsg->len;
+		iface->state = state_addr;
+		iface->result = 0;
+		iface->stopretry = 0;
+		if (pmsg->flags & I2C_M_RD)
+			iface->read_write = I2C_SMBUS_READ;
+		else
+			iface->read_write = I2C_SMBUS_WRITE;
+
+		/* Set up address and r/w bit */
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		
+		write_reg(reg_addr,
+			(addr << 1) |
+			((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+		/* Arm timeout */
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+
+		/* Start sending address & enable interrupt*/
+		write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+		write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+		wait_for_completion(&iface->complete);	
+
+		rc = iface->result;
+		if (rc == 0)
+			completed++;
+		DBG("transfer done, result: %d\n", rc);
+	}
+
+	/* Release sem */
+	up(&iface->sem);
+
+	return completed;
+}
+
+static u32
+keywest_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static void
+keywest_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void
+keywest_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm keywest_algorithm = {
+	.name		= "Keywest i2c",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= keywest_smbus_xfer,
+	.master_xfer	= keywest_xfer,
+	.functionality	= keywest_func,
+};
+
+
+static int __init
+create_iface(struct device_node* np)
+{
+	unsigned long steps, *psteps, *prate;
+	unsigned bsteps, tsize, i, nchan, addroffset;
+	struct keywest_iface* iface;
+	int error;
+
+	psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+	steps = psteps ? (*psteps) : 0x10;
+
+	/* Hrm... maybe we can be smarter here */
+	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
+		steps >>= 1;
+
+	if (!strcmp(np->parent->name, "uni-n")) {
+		nchan = 2;
+		addroffset = 3;
+	} else {
+		addroffset = 0;
+		nchan = 1;
+	}
+
+	tsize = sizeof(struct keywest_iface) +
+		(sizeof(struct keywest_chan) + 4) * nchan;
+	iface = kmalloc(tsize, GFP_KERNEL);
+	if (iface == NULL) {
+		printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
+		return -ENOMEM;
+	}
+	memset(iface, 0, tsize);
+	init_MUTEX(&iface->sem);
+	spin_lock_init(&iface->lock);
+	init_completion(&iface->complete);
+	iface->bsteps = bsteps;
+	iface->chan_count = nchan;
+	iface->state = state_idle;
+	iface->irq = np->intrs[0].line;
+	iface->channels = (struct keywest_chan *)
+		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
+	iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
+						np->addrs[0].size);
+	if (iface->base == 0) {
+		printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
+		kfree(iface);
+		return -ENOMEM;
+	}
+
+	init_timer(&iface->timeout_timer);
+	iface->timeout_timer.function = keywest_timeout;
+	iface->timeout_timer.data = (unsigned long)iface;
+
+	/* Select interface rate */
+	iface->cur_mode = KW_I2C_MODE_100KHZ;
+	prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+	if (prate) switch(*prate) {
+	case 100:
+		iface->cur_mode = KW_I2C_MODE_100KHZ;
+		break;
+	case 50:
+		iface->cur_mode = KW_I2C_MODE_50KHZ;
+		break;
+	case 25:
+		iface->cur_mode = KW_I2C_MODE_25KHZ;
+		break;
+	default:
+		printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
+			*prate);
+	}
+	
+	/* Select standard mode by default */
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	
+	/* Write mode */
+	write_reg(reg_mode, iface->cur_mode);
+	
+	/* Switch interrupts off & clear them*/
+	write_reg(reg_ier, 0x00);
+	write_reg(reg_isr, KW_I2C_IRQ_MASK);
+
+	/* Request chip interrupt */	
+	error = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
+	if (error) {
+		printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
+		iounmap((void *)iface->base);
+		kfree(iface);
+		return -ENODEV;
+	}
+
+	for (i=0; i<nchan; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		
+		sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
+		chan->iface = iface;
+		chan->chan_no = i;
+		chan->adapter.id = I2C_ALGO_SMBUS;
+		chan->adapter.algo = &keywest_algorithm;
+		chan->adapter.algo_data = NULL;
+		chan->adapter.inc_use = keywest_inc;
+		chan->adapter.dec_use = keywest_dec;
+		chan->adapter.client_register = NULL;
+		chan->adapter.client_unregister = NULL;
+		chan->adapter.data = chan;
+
+		error = i2c_add_adapter(&chan->adapter);
+		if (error) {
+			printk("i2c-keywest.c: Adapter %s registration failed\n",
+				chan->adapter.name);
+			chan->adapter.data = NULL;
+		}
+	}
+
+	printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
+		np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+		
+	iface->next = ifaces;
+	ifaces = iface;
+	return 0;
+}
+
+static void __exit
+dispose_iface(struct keywest_iface *iface)
+{
+	int i, error;
+	
+	ifaces = iface->next;
+
+	/* Make sure we stop all activity */
+	down(&iface->sem);
+	spin_lock_irq(&iface->lock);
+	while (iface->state != state_idle) {
+		spin_unlock_irq(&iface->lock);
+		set_task_state(current,TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/10);
+		spin_lock_irq(&iface->lock);
+	}
+	iface->state = state_dead;
+	spin_unlock_irq(&iface->lock);
+	free_irq(iface->irq, iface);
+	up(&iface->sem);
+
+	/* Release all channels */
+	for (i=0; i<iface->chan_count; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		if (!chan->adapter.data)
+			continue;
+		i2c_del_adapter(&chan->adapter);
+		chan->adapter.data = NULL;
+		/* We aren't that prepared to deal with this... */
+		if (error)
+			printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
+	}
+	iounmap((void *)iface->base);
+	kfree(iface);
+}
+
+static int __init i2c_keywest_init(void)
+{
+	struct device_node *np;
+	int error = -ENODEV;
+	
+	np = find_compatible_devices("i2c", "keywest");
+	while (np != 0) {
+		if (np->n_addrs >= 1 && np->n_intrs >= 1)
+			error = create_iface(np);
+		np = np->next;
+	}
+	if (ifaces)
+		error = 0;
+	return error;
+}
+
+static void __exit i2c_keywest_exit(void)
+{
+	while(ifaces)
+		dispose_iface(ifaces);
+}
+
+module_init(i2c_keywest_init);
+module_exit(i2c_keywest_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis645.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis645.c	(revision 4064)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-sis645.c	(revision 4064)
@@ -0,0 +1,605 @@
+/*
+    sis645.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.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 module must be considered BETA unless and until
+    the chipset manufacturer releases a datasheet.
+
+    The register definitions are based on the SiS630.
+    The method for *finding* the registers is based on trial and error.
+
+    A history of changes to this file is available by SVN:
+    http://www.lm-sensors.org/wiki/Download
+*/
+
+/*   25th March 2004
+     Support for Sis655 chipsets added by Ken Healy
+*/
+
+/*
+    Note: we assume there can only be one SiS645 with one SMBus interface
+*/
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+#define DRV_NAME "i2c-sis645"
+
+/* SiS645DX north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_646
+#define PCI_DEVICE_ID_SI_646 0x0646
+#endif
+
+/* SiS648 north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_648
+#define PCI_DEVICE_ID_SI_648 0x0648
+#endif
+
+/* SiS650 north bridge (defined in 2.4.19) */
+#ifndef PCI_DEVICE_ID_SI_650
+#define PCI_DEVICE_ID_SI_650 0x0650
+#endif
+
+/* SiS651 north bridge (defined in 2.4.21)*/
+#ifndef PCI_DEVICE_ID_SI_651
+#define PCI_DEVICE_ID_SI_651 0x0651
+#endif
+
+/* SiS655 north bridge (defined in 2.4.22)*/
+#ifndef PCI_DEVICE_ID_SI_655
+#define PCI_DEVICE_ID_SI_655 0x0655
+#endif
+
+/* SiS746 north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_746
+#define PCI_DEVICE_ID_SI_746 0x0746
+#endif
+
+/* SiS85C503/5513 (LPC Bridge) */
+#ifndef PCI_DEVICE_ID_SI_LPC
+#define PCI_DEVICE_ID_SI_LPC 0x0018
+#endif
+
+/* SiS961 south bridge */
+#ifndef PCI_DEVICE_ID_SI_961
+#define PCI_DEVICE_ID_SI_961 0x0961
+#endif
+
+/* SiS962 south bridge */
+#ifndef PCI_DEVICE_ID_SI_962
+#define PCI_DEVICE_ID_SI_962 0x0962
+#endif
+
+/* SiS963 south bridge */
+#ifndef PCI_DEVICE_ID_SI_963
+#define PCI_DEVICE_ID_SI_963 0x0963
+#endif
+
+/* SMBus ID */
+#ifndef PCI_DEVICE_ID_SI_SMBUS
+#define PCI_DEVICE_ID_SI_SMBUS 0x16
+#endif
+
+/* base address register in PCI config space */
+#define SIS645_BAR 0x04
+
+/* SiS645 SMBus registers */
+#define SMB_STS      0x00
+#define SMB_EN       0x01
+#define SMB_CNT      0x02
+#define SMB_HOST_CNT 0x03
+#define SMB_ADDR     0x04
+#define SMB_CMD      0x05
+#define SMB_PCOUNT   0x06
+#define SMB_COUNT    0x07
+#define SMB_BYTE     0x08
+#define SMB_DEV_ADDR 0x10
+#define SMB_DB0      0x11
+#define SMB_DB1      0x12
+#define SMB_SAA      0x13
+
+/* register count for request_region */
+#define SMB_IOSIZE 0x20
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* SiS645 SMBus constants */
+#define SIS645_QUICK      0x00
+#define SIS645_BYTE       0x01
+#define SIS645_BYTE_DATA  0x02
+#define SIS645_WORD_DATA  0x03
+#define SIS645_PROC_CALL  0x04
+#define SIS645_BLOCK_DATA 0x05
+
+static struct pci_driver sis645_driver;
+static struct i2c_adapter sis645_adapter;
+static u16 sis645_smbus_base = 0;
+
+static inline u8 sis645_read(u8 reg)
+{
+	return inb(sis645_smbus_base + reg) ;
+}
+
+static inline void sis645_write(u8 reg, u8 data)
+{
+	outb(data, sis645_smbus_base + reg) ;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/* Turns on SMBus device if it is not; return 0 iff successful
+ */
+static int __devinit sis645_enable_smbus(struct pci_dev *dev)
+{
+	u8 val = 0;
+
+	pci_read_config_byte(dev, 0x77, &val);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG DRV_NAME ": Config byte was 0x%02x.\n", val);
+#endif
+
+	pci_write_config_byte(dev, 0x77, val & ~0x10);
+
+	pci_read_config_byte(dev, 0x77, &val);
+
+	if (val & 0x10) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Config byte stuck!\n");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Builds the basic pci_dev for SiS645 SMBus
+ */
+static int __devinit sis645_build_dev(struct pci_dev **smbus_dev,
+			    struct pci_dev *bridge_dev)
+{
+	struct pci_dev temp_dev;
+	u16 vid = 0, did = 0;
+	int ret;
+
+	/* fill in the device structure for search */
+	memset(&temp_dev, 0, sizeof(temp_dev));
+	temp_dev.bus = bridge_dev->bus;
+	temp_dev.sysdata = bridge_dev->bus->sysdata;
+	temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL;
+
+	/* the SMBus device is function 1 on the same unit as the ISA bridge */
+	temp_dev.devfn = bridge_dev->devfn + 1;
+
+	/* query to make sure */
+	ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid);
+	if (ret || PCI_VENDOR_ID_SI != vid) {
+		printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
+		return ret;
+	}
+	temp_dev.vendor = vid;
+
+	ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did);
+	if (ret || PCI_DEVICE_ID_SI_SMBUS != did) {
+		printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
+		return ret;
+	}
+	temp_dev.device = did;
+
+	/* ok, we've got it... request some memory and finish it off */
+	*smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC);
+	if (NULL == *smbus_dev) {
+		printk(KERN_ERR DRV_NAME ": Out of memory!\n");
+		return -ENOMEM;
+	}
+
+	**smbus_dev = temp_dev;
+	
+	ret = pci_setup_device(*smbus_dev);
+	if (ret) {
+		printk(KERN_ERR DRV_NAME ": pci_setup_device failed (0x%08x)\n",ret);
+	}
+	return ret;
+}
+
+/* See if a SMBus can be found, and enable it if possible.
+ */
+static int __devinit sis645_hotplug_smbus(void)
+{
+	int ret;
+	struct pci_dev *smbus_dev, *bridge_dev ;
+
+	if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_961, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS961 south bridge.\n");
+
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_962, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS962 [MuTIOL Media IO].\n");
+
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_963, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS963 [MuTIOL Media IO].\n");
+		
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_503, NULL)) ||
+		(bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_LPC, NULL))) {
+
+		printk(KERN_INFO DRV_NAME ": Found SiS south bridge in compatability mode(?)\n");
+
+		/* look for known compatible north bridges */
+		if ((NULL == pci_find_device(PCI_VENDOR_ID_SI, 
+				PCI_DEVICE_ID_SI_645, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_646, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_648, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_650, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_651, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_655, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_735, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_745, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_746, NULL))) {
+			printk(KERN_ERR DRV_NAME ": Can't find suitable host bridge!\n");
+			return -ENODEV;
+		}
+	} else {
+		printk(KERN_ERR DRV_NAME ": Can't find suitable south bridge!\n");
+		return -ENODEV;
+	}
+
+	/* if we get this far, we think the smbus device is present */
+
+	if ((ret = sis645_enable_smbus(bridge_dev)))
+		return ret;
+
+	if ((ret = sis645_build_dev(&smbus_dev, bridge_dev)))
+		return ret;
+
+	if ((ret = pci_enable_device(smbus_dev))) {
+		printk(KERN_ERR DRV_NAME ": Can't pci_enable SMBus device!"
+			" (0x%08x)\n", ret);
+		return ret;
+	}
+
+	pci_insert_device(smbus_dev, smbus_dev->bus);
+
+	return 0;
+}
+#endif /* CONFIG_HOTPLUG */
+
+/* Execute a SMBus transaction.
+   int size is from SIS645_QUICK to SIS645_BLOCK_DATA
+ */
+static int sis645_transaction(int size)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": SMBus busy (0x%02x). Resetting...\n",
+				temp);
+#endif
+
+		/* kill the transaction */
+		sis645_write(SMB_HOST_CNT, 0x20);
+
+		/* check it again */
+		if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
+#ifdef DEBUG
+			printk(KERN_DEBUG DRV_NAME ": Failed! (0x%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG DRV_NAME ": Successful!\n");
+#endif
+		}
+	}
+
+	/* Turn off timeout interrupts, set fast host clock */
+	sis645_write(SMB_CNT, 0x20);
+
+	/* clear all (sticky) status flags */
+	temp = sis645_read(SMB_STS);
+	sis645_write(SMB_STS, temp & 0x1e);
+
+	/* start the transaction by setting bit 4 and size bits */
+	sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = sis645_read(SMB_STS);
+	} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		printk(KERN_DEBUG DRV_NAME ": SMBus Timeout! (0x%02x)\n",temp);
+		result = -1;
+	}
+
+	/* device error - probably missing ACK */
+	if (temp & 0x02) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Failed bus transaction!\n");
+#endif
+		result = -1;
+	}
+
+	/* bus collision */
+	if (temp & 0x04) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Bus collision!\n");
+#endif
+		result = -1;
+	}
+
+	/* Finish up by resetting the bus */
+	sis645_write(SMB_STS, temp);
+	if ((temp = sis645_read(SMB_STS))) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Failed reset at end of transaction!"
+				" (0x%02x)\n", temp);
+#endif
+	}
+
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 sis645_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		size = SIS645_QUICK;
+		break;
+
+	case I2C_SMBUS_BYTE:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		if (read_write == I2C_SMBUS_WRITE)
+			sis645_write(SMB_CMD, command);
+		size = SIS645_BYTE;
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis645_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE)
+			sis645_write(SMB_BYTE, data->byte);
+		size = SIS645_BYTE_DATA;
+		break;
+
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_WORD_DATA:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis645_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			sis645_write(SMB_BYTE, data->word & 0xff);
+			sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
+		}
+		size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA);
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		/* TO DO: */
+		printk(KERN_INFO DRV_NAME ": SMBus block not implemented!\n");
+		return -1;
+		break;
+
+	default:
+		printk(KERN_INFO DRV_NAME ": Unsupported I2C size\n");
+		return -1;
+		break;
+	}
+
+	if (sis645_transaction(size))
+		return -1;
+
+	if ((size != SIS645_PROC_CALL) &&
+			((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK)))
+		return 0;
+
+	switch (size) {
+	case SIS645_BYTE:
+	case SIS645_BYTE_DATA:
+		data->byte = sis645_read(SMB_BYTE);
+		break;
+
+	case SIS645_WORD_DATA:
+	case SIS645_PROC_CALL:
+		data->word = sis645_read(SMB_BYTE) +
+				(sis645_read(SMB_BYTE + 1) << 8);
+		break;
+	}
+	return 0;
+}
+
+static void sis645_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void sis645_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static u32 sis645_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis645_access,
+	.functionality	= sis645_func,
+};
+
+static struct i2c_adapter sis645_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645,
+	.algo		= &smbus_algorithm,
+	.inc_use	= sis645_inc,
+	.dec_use	= sis645_dec,
+};
+
+static struct pci_device_id sis645_ids[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_SI,
+		.device = PCI_DEVICE_ID_SI_SMBUS,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis645_probe(struct pci_dev *dev,
+				const struct pci_device_id *id)
+{
+	u16 ww = 0;
+	int retval;
+
+	if (sis645_smbus_base) {
+		dev_err(dev, "Only one device supported.\n");
+		return -EBUSY;
+	}
+
+	pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww);
+	if (PCI_CLASS_SERIAL_SMBUS != ww) {
+		dev_err(dev, "Unsupported device class 0x%04x!\n", ww);
+		return -ENODEV;
+	}
+
+	sis645_smbus_base = pci_resource_start(dev, SIS645_BAR);
+	if (!sis645_smbus_base) {
+		dev_err(dev, "SiS645 SMBus base address "
+			"not initialized!\n");
+		return -EINVAL;
+	}
+	dev_info(dev, "SiS645 SMBus base address: 0x%04x\n",
+			sis645_smbus_base);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	if (!request_region(sis645_smbus_base, SMB_IOSIZE,
+			    sis645_driver.name)) {
+		dev_err(dev, "SMBus registers 0x%04x-0x%04x "
+			"already in use!\n", sis645_smbus_base,
+			sis645_smbus_base + SMB_IOSIZE - 1);
+
+		sis645_smbus_base = 0;
+		return -EINVAL;
+	}
+
+	sprintf(sis645_adapter.name, "SiS645 SMBus adapter at 0x%04x",
+			sis645_smbus_base);
+
+	if ((retval = i2c_add_adapter(&sis645_adapter))) {
+		dev_err(dev, "Couldn't register adapter!\n");
+		release_region(sis645_smbus_base, SMB_IOSIZE);
+		sis645_smbus_base = 0;
+	}
+
+	return retval;
+}
+
+static void __devexit sis645_remove(struct pci_dev *dev)
+{
+	if (sis645_smbus_base) {
+		i2c_del_adapter(&sis645_adapter);
+		release_region(sis645_smbus_base, SMB_IOSIZE);
+		sis645_smbus_base = 0;
+	}
+}
+
+static struct pci_driver sis645_driver = {
+	.name		= "sis645 smbus",
+	.id_table	= sis645_ids,
+	.probe		= sis645_probe,
+	.remove		= __devexit_p(sis645_remove),
+};
+
+static int __init i2c_sis645_init(void)
+{
+	printk(KERN_INFO DRV_NAME ".o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	/* if the required device id is not present, try to HOTPLUG it first */
+	if (!pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS, NULL)) {
+
+		printk(KERN_INFO DRV_NAME ": "
+			"Attempting to enable SiS645 SMBus device\n");
+
+#ifdef CONFIG_HOTPLUG
+		sis645_hotplug_smbus();
+#else
+		printk(KERN_INFO DRV_NAME ": "
+			"Requires kernel with CONFIG_HOTPLUG, sorry!\n");
+#endif
+	}
+
+	return pci_module_init(&sis645_driver);
+}
+
+static void __exit i2c_sis645_exit(void)
+{
+	pci_unregister_driver(&sis645_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("SiS645 SMBus driver");
+MODULE_LICENSE("GPL");
+
+/* Register initialization functions using helper macros */
+module_init(i2c_sis645_init);
+module_exit(i2c_sis645_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-piix4.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-piix4.c	(revision 4053)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-piix4.c	(revision 4053)
@@ -0,0 +1,576 @@
+/*
+    piix4.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.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:
+	Intel PIIX4, 440MX
+	Serverworks OSB4, CSB5, CSB6, HT-1000
+	ATI IXP200, IXP300, IXP400
+	SMSC Victory66
+
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/apm_bios.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+
+struct sd {
+	const unsigned short mfr;
+	const unsigned short dev;
+	const unsigned char fn;
+	const char *name;
+};
+
+/* PIIX4 SMBus address offsets */
+#define SMBHSTSTS (0 + piix4_smba)
+#define SMBHSLVSTS (1 + piix4_smba)
+#define SMBHSTCNT (2 + piix4_smba)
+#define SMBHSTCMD (3 + piix4_smba)
+#define SMBHSTADD (4 + piix4_smba)
+#define SMBHSTDAT0 (5 + piix4_smba)
+#define SMBHSTDAT1 (6 + piix4_smba)
+#define SMBBLKDAT (7 + piix4_smba)
+#define SMBSLVCNT (8 + piix4_smba)
+#define SMBSHDWCMD (9 + piix4_smba)
+#define SMBSLVEVT (0xA + piix4_smba)
+#define SMBSLVDAT (0xC + piix4_smba)
+
+/* count for request_region */
+#define SMBIOSIZE 8
+
+/* PCI Address Constants */
+#define SMBBA     0x090
+#define SMBHSTCFG 0x0D2
+#define SMBSLVC   0x0D3
+#define SMBSHDW1  0x0D4
+#define SMBSHDW2  0x0D5
+#define SMBREV    0x0D6
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+#define  ENABLE_INT9 0
+
+/* PIIX4 constants */
+#define PIIX4_QUICK      0x00
+#define PIIX4_BYTE       0x04
+#define PIIX4_BYTE_DATA  0x08
+#define PIIX4_WORD_DATA  0x0C
+#define PIIX4_BLOCK_DATA 0x14
+
+/* insmod parameters */
+
+/* If force is set to anything different from 0, we forcibly enable the
+   PIIX4. DANGEROUS! */
+static int force = 0;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the PIIX4 at the given address. VERY DANGEROUS! */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the PIIX4 at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+static int piix4_transaction(void);
+
+static unsigned short piix4_smba = 0;
+static struct pci_driver piix4_driver;
+
+#ifdef CONFIG_X86
+/*
+ * Get DMI information.
+ */
+
+static int __devinit ibm_dmi_probe(void)
+{
+	extern int is_unsafe_smbus;
+	return is_unsafe_smbus;
+}
+#endif
+
+/* Detect whether a PIIX4 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
+				const struct pci_device_id *id)
+{
+	unsigned char temp;
+
+	/* match up the function */
+	if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data)
+		return -ENODEV;
+
+	printk(KERN_INFO "Found %s device\n", PIIX4_dev->name);
+
+#ifdef CONFIG_X86
+	if(ibm_dmi_probe() && PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
+		printk(KERN_ERR "i2c-piix4.o: IBM system detected; this module "
+			"may corrupt your serial eeprom! Refusing to load "
+			"module!\n");
+		return -EPERM;
+	}
+#endif
+
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		piix4_smba = force_addr & 0xfff0;
+		force = 0;
+	} else {
+		pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
+		piix4_smba &= 0xfff0;
+		if(piix4_smba == 0) {
+			printk(KERN_ERR "i2c-piix4.o: SMB base address "
+				"uninitialized - upgrade BIOS or use "
+				"force_addr=0xaddr\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
+		printk(KERN_ERR "i2c-piix4.o: SMB region 0x%x already in "
+			"use!\n", piix4_smba);
+		return -ENODEV;
+	}
+
+	pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
+
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the PIIX4 first. */
+	if (force_addr) {
+		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
+		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01);
+		printk(KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to "
+			"new address %04x!\n", piix4_smba);
+	} else if ((temp & 1) == 0) {
+		if (force) {
+			/* This should never need to be done, but has been
+			 * noted that many Dell machines have the SMBus
+			 * interface on the PIIX4 disabled!? NOTE: This assumes
+			 * I/O space and other allocations WERE done by the
+			 * Bios!  Don't complain if your hardware does weird
+			 * things after enabling this. :') Check for Bios
+			 * updates before resorting to this.
+			 */
+			pci_write_config_byte(PIIX4_dev, SMBHSTCFG,
+					      temp | 1);
+			printk(KERN_NOTICE "i2c-piix4.o: WARNING: SMBus "
+				"interface has been FORCEFULLY ENABLED!\n");
+		} else {
+			printk(KERN_ERR "i2c-piix4.o: Host SMBus controller "
+				"not enabled!\n");
+			release_region(piix4_smba, SMBIOSIZE);
+			piix4_smba = 0;
+			return -ENODEV;
+		}
+	}
+
+#ifdef DEBUG
+	if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2))
+		printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for "
+			"SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# "
+			"for SMBus.\n");
+	else
+		printk(KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration "
+			"(or code out of date)!\n");
+
+	pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
+	printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp);
+	printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba);
+#endif				/* DEBUG */
+
+	return 0;
+}
+
+
+/* Another internally used function */
+int piix4_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
+	     "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
+	     inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+#endif
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting...\n",
+		       temp);
+#endif
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+			printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n");
+#endif
+		}
+	}
+
+	/* start the transaction by setting bit 6 */
+	outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+#ifdef DEBUG
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n");
+		result = -1;
+	}
+#endif
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		printk
+		    (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n"
+		     "reset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+#ifdef DEBUG
+		printk(KERN_ERR "i2c-piix4.o: Error: no response!\n");
+#endif
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+#ifdef DEBUG
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		printk
+		    (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+	}
+	printk
+	    (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
+	     "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
+	     inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+#endif
+	return result;
+}
+
+/* Return -1 on error. */
+s32 piix4_access(struct i2c_adapter * adap, u16 addr,
+		 unsigned short flags, char read_write,
+		 u8 command, int size, union i2c_smbus_data * data)
+{
+	int i, len;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = PIIX4_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = PIIX4_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = PIIX4_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = PIIX4_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outb_p(len, SMBHSTDAT0);
+			i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = PIIX4_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-piix4.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (piix4_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case PIIX4_BYTE:	/* Where is the result put? I assume here it is in
+				   SMBHSTDAT0 but it might just as well be in the
+				   SMBHSTCMD. No clue in the docs */
+
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case PIIX4_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case PIIX4_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case PIIX4_BLOCK_DATA:
+		data->block[0] = inb_p(SMBHSTDAT0);
+		i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMBBLKDAT);
+		break;
+	}
+	return 0;
+}
+
+static void piix4_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void piix4_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u32 piix4_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= piix4_access,
+	.functionality	= piix4_func,
+};
+
+static struct i2c_adapter piix4_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4,
+	.algo		= &smbus_algorithm,
+	.inc_use	= piix4_inc,
+	.dec_use	= piix4_dec,
+};
+
+#define PCI_DEVICE_ID_ATI_IXP200_SMBUS	0x4353
+#define PCI_DEVICE_ID_ATI_IXP300_SMBUS	0x4363
+#define PCI_DEVICE_ID_ATI_IXP400_SMBUS	0x4372
+
+#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB6
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203
+#endif
+
+#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205
+
+static struct pci_device_id piix4_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82371AB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	3
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_ATI,
+		.device =	PCI_DEVICE_ID_ATI_IXP200_SMBUS,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_ATI,
+		.device =	PCI_DEVICE_ID_ATI_IXP300_SMBUS,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_ATI,
+		.device =	PCI_DEVICE_ID_ATI_IXP400_SMBUS,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_OSB4,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_CSB5,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_CSB6,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_HT1000SB,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82443MX_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	3,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_EFAR,
+		.device =	PCI_DEVICE_ID_EFAR_SLC90E66_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{ 0, }
+};
+
+static int __devinit piix4_probe(struct pci_dev *dev,
+				const struct pci_device_id *id)
+{
+	int retval;
+
+	retval = piix4_setup(dev, id);
+	if (retval)
+		return retval;
+
+	sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x",
+		piix4_smba);
+
+	if ((retval = i2c_add_adapter(&piix4_adapter))) {
+		printk(KERN_ERR "i2c-piix4.o: Couldn't register adapter!\n");
+		release_region(piix4_smba, SMBIOSIZE);
+		piix4_smba = 0;
+	}
+
+	return retval;
+}
+
+static void __devexit piix4_remove(struct pci_dev *dev)
+{
+	if (piix4_smba) {
+		i2c_del_adapter(&piix4_adapter);
+		release_region(piix4_smba, SMBIOSIZE);
+		piix4_smba = 0;
+	}
+}
+
+static struct pci_driver piix4_driver = {
+	.name		= "piix4 smbus",
+	.id_table	= piix4_ids,
+	.probe		= piix4_probe,
+	.remove		= __devexit_p(piix4_remove),
+};
+
+static int __init i2c_piix4_init(void)
+{
+	printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&piix4_driver);
+}
+
+static void __exit i2c_piix4_exit(void)
+{
+	pci_unregister_driver(&piix4_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
+		"Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("PIIX4 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_piix4_init);
+module_exit(i2c_piix4_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-nforce2.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-nforce2.c	(revision 4054)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-nforce2.c	(revision 4054)
@@ -0,0 +1,432 @@
+/*
+    SMBus driver for nVidia nForce2 MCP
+
+    Copyright (c) 2003  Hans-Frieder Vogt <hfvogt@arcor.de>,
+    Based on
+    SMBus 2.0 driver for AMD-8111 IO-Hub
+    Copyright (c) 2002 Vojtech Pavlik
+
+    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.
+*/
+
+/*
+    SUPPORTED DEVICES		PCI ID
+    nForce2 MCP			0064
+    nForce2 Ultra 400 MCP	0084
+    nForce3 Pro150 MCP		00D4
+    nForce3 250Gb MCP		00E4
+    nForce4 MCP			0052
+    nForce4 MCP-04		0034
+    nForce4 MCP51		0264
+    nForce4 MCP55		0368
+
+    This driver supports the 2 SMBuses that are included in the MCP of the
+    nForce2/3/4 chipsets.
+*/
+
+/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>");
+MODULE_DESCRIPTION("nForce2 SMBus driver");
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS	0x0064
+#endif
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS	0x0084
+#endif
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS	0x00D4
+#endif
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS	0x00E4
+#endif
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS	0x0052
+#endif
+
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS	0x0034
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS	0x0264
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS	0x0368
+
+
+struct nforce2_smbus {
+	struct pci_dev *dev;
+	struct i2c_adapter adapter;
+	int base;
+	int size;
+};
+
+
+/*
+ * nVidia nForce2 SMBus control register definitions
+ * (Newer incarnations use standard BARs 4 and 5 instead)
+ */
+#define NFORCE_PCI_SMB1	0x50
+#define NFORCE_PCI_SMB2	0x54
+
+
+/*
+ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
+ */
+#define NVIDIA_SMB_PRTCL	(smbus->base + 0x00)	/* protocol, PEC */
+#define NVIDIA_SMB_STS		(smbus->base + 0x01)	/* status */
+#define NVIDIA_SMB_ADDR		(smbus->base + 0x02)	/* address */
+#define NVIDIA_SMB_CMD		(smbus->base + 0x03)	/* command */
+#define NVIDIA_SMB_DATA		(smbus->base + 0x04)	/* 32 data registers */
+#define NVIDIA_SMB_BCNT		(smbus->base + 0x24)	/* number of data bytes */
+#define NVIDIA_SMB_ALRM_A	(smbus->base + 0x25)	/* alarm address */
+#define NVIDIA_SMB_ALRM_D	(smbus->base + 0x26)	/* 2 bytes alarm data */
+
+#define NVIDIA_SMB_STS_DONE	0x80
+#define NVIDIA_SMB_STS_ALRM	0x40
+#define NVIDIA_SMB_STS_RES	0x20
+#define NVIDIA_SMB_STS_STATUS	0x1f
+
+#define NVIDIA_SMB_PRTCL_WRITE			0x00
+#define NVIDIA_SMB_PRTCL_READ			0x01
+#define NVIDIA_SMB_PRTCL_QUICK			0x02
+#define NVIDIA_SMB_PRTCL_BYTE			0x04
+#define NVIDIA_SMB_PRTCL_BYTE_DATA		0x06
+#define NVIDIA_SMB_PRTCL_WORD_DATA		0x08
+#define NVIDIA_SMB_PRTCL_BLOCK_DATA		0x0a
+#define NVIDIA_SMB_PRTCL_PROC_CALL		0x0c
+#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
+#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA		0x4a
+#define NVIDIA_SMB_PRTCL_PEC			0x80
+
+static struct pci_driver nforce2_driver;
+
+static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write,
+		       u8 command, int size, union i2c_smbus_data *data);
+/*
+static int nforce2_block_transaction(union i2c_smbus_data *data,
+				  char read_write, int i2c_enable);
+ */
+static u32 nforce2_func(struct i2c_adapter *adapter);
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name = "Non-I2C SMBus adapter",
+	.id = I2C_ALGO_SMBUS,
+	.smbus_xfer = nforce2_access,
+	.functionality = nforce2_func,
+};
+
+/* Return -1 on error. See smbus.h for more information */
+s32 nforce2_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+		char read_write, u8 command, int size,
+		union i2c_smbus_data * data)
+{
+	struct nforce2_smbus *smbus = adap->algo_data;
+	unsigned char protocol, pec, temp;
+	unsigned char len = 0; /* to keep the compiler quiet */
+	int i;
+
+	protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : NVIDIA_SMB_PRTCL_WRITE;
+	pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_QUICK:
+			protocol |= NVIDIA_SMB_PRTCL_QUICK;
+			read_write = I2C_SMBUS_WRITE;
+			break;
+
+		case I2C_SMBUS_BYTE:
+			if (read_write == I2C_SMBUS_WRITE)
+				outb_p(command, NVIDIA_SMB_CMD);
+			protocol |= NVIDIA_SMB_PRTCL_BYTE;
+			break;
+
+		case I2C_SMBUS_BYTE_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE)
+				outb_p(data->byte, NVIDIA_SMB_DATA);
+			protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA;
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE) {
+				 outb_p(data->word, NVIDIA_SMB_DATA);
+				 outb_p(data->word >> 8, NVIDIA_SMB_DATA+1);
+			}
+			protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE) {
+				len = min_t(u8, data->block[0], 32);
+				outb_p(len, NVIDIA_SMB_BCNT);
+				for (i = 0; i < len; i++)
+					outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
+			}
+			protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
+			break;
+
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			len = min_t(u8, data->block[0], 32);
+			outb_p(command, NVIDIA_SMB_CMD);
+			outb_p(len, NVIDIA_SMB_BCNT);
+			if (read_write == I2C_SMBUS_WRITE)
+				for (i = 0; i < len; i++)
+					outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
+			protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA;
+			break;
+
+		case I2C_SMBUS_PROC_CALL:
+			printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_PROC_CALL not supported!\n");
+			return -1;
+
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_BLOCK_PROC_CALL not supported!\n");
+			return -1;
+
+		default:
+			printk(KERN_WARNING "i2c-nforce2.c: Unsupported transaction %d\n", size);
+			return -1;
+	}
+
+	outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR);
+	outb_p(protocol, NVIDIA_SMB_PRTCL);
+
+	temp = inb_p(NVIDIA_SMB_STS);
+
+	if (~temp & NVIDIA_SMB_STS_DONE) {
+		udelay(500);
+		temp = inb_p(NVIDIA_SMB_STS);
+	}
+	if (~temp & NVIDIA_SMB_STS_DONE) {
+		i2c_delay(HZ/100);
+		temp = inb_p(NVIDIA_SMB_STS);
+	}
+
+	if ((~temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) {
+		printk(KERN_DEBUG "i2c-nforce2.o: SMBus Timeout! (0x%02x)\n",
+		       temp);
+		return -1;
+	}
+
+	if (read_write == I2C_SMBUS_WRITE)
+		return 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			data->byte = inb_p(NVIDIA_SMB_DATA);
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+		/* case I2C_SMBUS_PROC_CALL: not supported */
+			data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+		/* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */
+			len = inb_p(NVIDIA_SMB_BCNT);
+			len = min_t(u8, len, 32);
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			for (i = 0; i < len; i++)
+				data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
+			data->block[0] = len;
+			break;
+	}
+
+	return 0;
+}
+
+
+u32 nforce2_func(struct i2c_adapter *adapter)
+{
+	/* other functionality might be possible, but is not tested */
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* |
+	    I2C_FUNC_SMBUS_BLOCK_DATA */;
+}
+
+static void nforce2_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void nforce2_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct pci_device_id nforce2_ids[] = {
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0 }
+};
+
+
+static int __devinit nforce2_probe_smb(struct pci_dev *dev, int bar,
+	int alt_reg, struct nforce2_smbus *smbus, char *name)
+{
+	int error;
+
+	smbus->base = pci_resource_start(dev, bar);
+	if (smbus->base) {
+		smbus->size = pci_resource_len(dev, bar);
+	} else {
+		/* Older incarnations of the device used non-standard BARs */
+		u16 iobase;
+
+		if (pci_read_config_word(dev, alt_reg, &iobase)
+		    != PCIBIOS_SUCCESSFUL) {
+			printk(KERN_ERR "i2c-nforce2.o: Error reading PCI "
+			       "config for %s\n", name);
+			return -1;
+		}
+
+		smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
+		smbus->size = 8;
+	}
+	smbus->dev = dev;
+
+	if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
+		printk (KERN_ERR "i2c-nforce2.o: Error requesting region %02x .. %02X for %s\n", smbus->base, smbus->base+smbus->size-1, name);
+		return -1;
+	}
+
+	sprintf(smbus->adapter.name, "SMBus nForce2 adapter at %04x", smbus->base);
+	smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_NFORCE2;
+	smbus->adapter.algo = &smbus_algorithm;
+	smbus->adapter.algo_data = smbus;
+	smbus->adapter.inc_use = nforce2_inc;
+	smbus->adapter.dec_use = nforce2_dec;
+
+	error = i2c_add_adapter(&smbus->adapter);
+	if (error) {
+		printk(KERN_WARNING "i2c-nforce2.o: Failed to register adapter.\n");
+		release_region(smbus->base, smbus->size);
+		return -1;
+	}
+	printk(KERN_INFO "i2c-nforce2.o: nForce2 SMBus adapter at %#x\n", smbus->base);
+	return 0;
+}
+
+
+static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct nforce2_smbus *smbuses;
+	int res1, res2;
+
+	/* we support 2 SMBus adapters */
+	if (!(smbuses = kmalloc(2*sizeof(struct nforce2_smbus), GFP_KERNEL)))
+		return -ENOMEM;
+	memset (smbuses, 0, 2*sizeof(struct nforce2_smbus));
+	pci_set_drvdata(dev, smbuses);
+
+	/* SMBus adapter 1 */
+	res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
+	if (res1 < 0) {
+		printk (KERN_ERR "i2c-nforce2.o: Error probing SMB1.\n");
+		smbuses[0].base = 0;	/* to have a check value */
+	}
+	/* SMBus adapter 2 */
+	res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
+	if (res2 < 0) {
+		printk (KERN_ERR "i2c-nforce2.o: Error probing SMB2.\n");
+		smbuses[1].base = 0;	/* to have a check value */
+	}
+	if ((res1 < 0) && (res2 < 0)) {
+		/* we did not find even one of the SMBuses, so we give up */
+		kfree(smbuses);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static void __devexit nforce2_remove(struct pci_dev *dev)
+{
+	struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev);
+
+	if (smbuses[0].base) {
+		i2c_del_adapter(&smbuses[0].adapter);
+		release_region(smbuses[0].base, smbuses[0].size);
+	}
+	if (smbuses[1].base) {
+		i2c_del_adapter(&smbuses[1].adapter);
+		release_region(smbuses[1].base, smbuses[1].size);
+	}
+	kfree(smbuses);
+}
+
+static struct pci_driver nforce2_driver = {
+	.name		= "nForce2 SMBus",
+	.id_table	= nforce2_ids,
+	.probe		= nforce2_probe,
+	.remove		= __devexit_p(nforce2_remove),
+};
+
+int __init nforce2_init(void)
+{
+	printk(KERN_INFO "i2c-nforce2.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&nforce2_driver);
+}
+
+void __exit nforce2_exit(void)
+{
+	pci_unregister_driver(&nforce2_driver);
+}
+
+module_init(nforce2_init);
+module_exit(nforce2_exit);
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmi.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmi.c	(revision 2987)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-ipmi.c	(revision 2987)
@@ -0,0 +1,251 @@
+/*
+    i2c-ipmi.c - Part of lm_sensors, Linux kernel modules for hardware
+            monitoring
+    Copyright (c) 2002 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 implements a "dummy" i2c adapter for clients to access the
+    BMC via IPMI messages. Supports only one BMC and one client for now!
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/ipmi.h>
+#include "version.h"
+
+
+static u32 i2c_ipmi_func(struct i2c_adapter *adapter);
+static int bmcclient_i2c_send_message(struct i2c_adapter *, char *, int);
+
+static void i2c_ipmi_inc_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void i2c_ipmi_dec_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* I2C Data */
+static struct i2c_algorithm i2c_ipmi_algorithm = {
+	.name = "IPMI algorithm",
+	.id = I2C_ALGO_IPMI,
+	.slave_send = &bmcclient_i2c_send_message,
+	.functionality = &i2c_ipmi_func,
+};
+
+static struct i2c_adapter i2c_ipmi_adapter = {
+	.name		= "IPMI adapter",
+	.id		= I2C_ALGO_IPMI | I2C_HW_IPMI,
+	.algo		= &i2c_ipmi_algorithm,
+	.inc_use	= &i2c_ipmi_inc_use,
+	.dec_use	= &i2c_ipmi_dec_use,
+};
+
+/* IPMI Data */
+static ipmi_user_t i2c_ipmi_user;
+static unsigned char ipmi_version_major;
+static unsigned char ipmi_version_minor;
+static const char msgdata[IPMI_MAX_ADDR_SIZE];   /* ?? */
+static struct ipmi_addr address = {
+	IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
+	IPMI_BMC_CHANNEL,
+	{0}
+};	/* send address */
+static int interfaces;		/* number of BMC's found */
+
+
+/* Dummy adapter... */
+static u32 i2c_ipmi_func(struct i2c_adapter *adapter)
+{
+	return 0;
+}
+
+/************** Message Sending **************/
+
+static int find_client(struct i2c_client * client)
+{
+	int i;
+
+	for (i = 0; i < I2C_CLIENT_MAX; i++)
+		if (client == i2c_ipmi_adapter.clients[i])
+			return i;
+	return -1;
+}
+
+static void ipmi_i2c_send_message(int id, struct ipmi_msg * msg)
+{
+#ifdef IPMI_RESPONSE_RESPONSE_TYPE
+	ipmi_request(i2c_ipmi_user, &address, (long) id, msg, NULL, 0);
+#else
+	ipmi_request(i2c_ipmi_user, &address, (long) id, msg, 0);
+#endif
+}
+
+/* This is the message send function exported to the client
+   via the i2c_adapter struct.
+   We use the existing (but unused) slave_send function pointer.
+   Hence the ugly casts. */
+static int bmcclient_i2c_send_message(struct i2c_adapter *clnt,
+                                      char * mesg, int id)
+{
+	struct ipmi_msg *msg = (struct ipmi_msg *) mesg;
+	struct i2c_client *client = (struct i2c_client *) clnt;
+        int clientid;
+	
+#ifdef DEBUG
+	if(msg->data == NULL)
+		printk(KERN_INFO "i2c-ipmi.o: Send 0x%x\n", msg->cmd);
+	else
+		printk(KERN_INFO "i2c-ipmi.o: Send 0x%x 0x%x 0x%x\n", msg->cmd, msg->data[0], msg->data[1]);
+#endif
+	/* save the client number in the upper 8 bits of the message id */
+	if((clientid = find_client(client)) < 0) {
+		printk(KERN_WARNING "i2c-ipmi.o: Request from unknown client\n");
+		return -1;      
+	}
+
+	id = (id & 0xffffff) | (clientid << 24);
+	ipmi_i2c_send_message(id, msg);
+	return 0;
+}
+
+/************** Message Receiving **************/
+
+static void ipmi_i2c_msg_handler(struct ipmi_recv_msg *msg,
+				  void            *handler_data)
+{
+	int rcvid = msg->msgid & 0xffffff;
+	int clientid = (msg->msgid >> 24) & 0xff;
+
+#ifdef DEBUG
+	if (msg->msg.data[0] != 0)
+		printk(KERN_WARNING "i2c-ipmi.o: Error 0x%x on cmd 0x%x/0x%x\n",
+		       msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
+#endif
+	/* Protect ourselves here; verify the client and its callback
+	   since the client may have gone away since
+	   the message was sent! */
+	if(clientid < I2C_CLIENT_MAX &&
+	   i2c_ipmi_adapter.clients[clientid] != NULL &&
+	   i2c_ipmi_adapter.clients[clientid]->driver->command != NULL)
+	   	(* i2c_ipmi_adapter.clients[clientid]->driver->command)
+		     (i2c_ipmi_adapter.clients[clientid], rcvid, msg);
+	else {
+		printk(KERN_WARNING "i2c-ipmi.o: Response for unknown client\n");
+		ipmi_free_recv_msg(msg);
+	}
+}
+
+static struct ipmi_user_hndl ipmi_hndlrs =
+{
+	.ipmi_recv_hndl           = ipmi_i2c_msg_handler,
+};
+
+/**************** Initialization ****************/
+
+/* callback for each BMC found */
+static void ipmi_register_bmc(int ipmi_intf)
+{
+	int error;
+
+	if(interfaces > 0) {	/* 1 max for now */
+		printk(KERN_INFO
+		       "i2c-ipmi.o: Additional IPMI interface %d not supported\n",
+		       ipmi_intf);
+		return;
+	}
+
+	error = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &i2c_ipmi_user);
+	if (error < 0) {
+		printk(KERN_ERR "i2c-ipmi.o: Unable to register with ipmi\n");
+		return;
+	}
+
+	error = i2c_add_adapter(&i2c_ipmi_adapter);
+	if (error) {
+		printk(KERN_ERR "i2c-ipmi.o: Adapter registration failed, "
+		       "module i2c-ipmi.o is not inserted\n.");
+		return;
+	}
+
+	ipmi_get_version(i2c_ipmi_user, &ipmi_version_major,
+	                 &ipmi_version_minor);
+	printk(KERN_INFO
+	       "i2c-ipmi.o: Registered IPMI interface %d with version %d.%d\n",
+	       ipmi_intf, ipmi_version_major, ipmi_version_minor);
+	interfaces++;
+}
+
+static void ipmi_new_smi(int if_num)
+{
+	ipmi_register_bmc(if_num);
+}
+
+static void ipmi_smi_gone(int if_num)
+{
+	if (interfaces >= 1) {
+		i2c_del_adapter(&i2c_ipmi_adapter);
+		ipmi_destroy_user(i2c_ipmi_user);
+		interfaces--;
+	}
+}
+
+static struct ipmi_smi_watcher smi_watcher =
+{
+	.new_smi  = ipmi_new_smi,
+	.smi_gone = ipmi_smi_gone
+};
+
+static int __init i2c_ipmi_init(void)
+{
+	int rv;
+
+	printk(KERN_INFO "i2c-ipmi.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	rv = ipmi_smi_watcher_register(&smi_watcher);
+	if (rv) {
+		printk(KERN_WARNING
+		       "ipmi_watchdog: can't register smi watcher\n");
+		return rv;
+	}
+
+	printk(KERN_INFO "i2c-ipmi.o: BMC access for i2c modules initialized.\n");
+	return 0;
+}
+
+
+static void __exit i2c_ipmi_exit(void)
+{
+	ipmi_smi_watcher_unregister(&smi_watcher);
+	ipmi_smi_gone(0);
+}
+
+MODULE_AUTHOR("M. D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("IPMI-BMC access through i2c");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ipmi_init);
+module_exit(i2c_ipmi_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/Module.mk
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/Module.mk	(revision 2971)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/Module.mk	(revision 2971)
@@ -0,0 +1,129 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := kernel/busses
+KERNELBUSSESDIR := $(MODULE_DIR)
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+# These targets are NOT included in 'mkpatch' ...
+KERNELBUSSESTARGETS :=
+ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; fi),1)
+#doesn't work yet
+#KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ipmb.o
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ipmi.o
+endif
+
+# These targets ARE included in 'mkpatch' ...
+ifneq ($(shell if grep -q '^CONFIG_I2C_ALI1535=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali1535.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_ALI15X3=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali15x3.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_ALI1563=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali1563.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_AMD756=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd756.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_AMD756_S4882=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd756-s4882.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_AMD8111=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd8111.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_HYDRA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-hydra.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_I801=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-i801.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_I810=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-i810.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_ISA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-isa.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_NFORCE2=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-nforce2.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_SIS5595=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis5595.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_SIS630=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis630.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_SIS645=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis645.o
+endif
+# don't compile dmi_scan unless x86 because it needs isa access
+ifneq ($(shell if grep -q '^CONFIG_I2C_PIIX4=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-piix4.o
+ifeq ($(shell if grep -q '^CONFIG_X86=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/dmi_scan.o
+endif
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_SAVAGE4=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-savage4.o
+endif
+# don't compile unless alpha because of kernel include-file dependencies
+ifeq ($(MACHINE),alpha)
+ifneq ($(shell if grep -q '^CONFIG_I2C_TSUNAMI=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-tsunami.o
+endif
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_VIA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-via.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_VIAPRO=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-viapro.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_VOODOO3=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-voodoo3.o
+endif
+
+# Include all dependency files
+INCLUDEFILES += $(KERNELBUSSESTARGETS:.o=.d)
+
+all-kernel-busses: $(KERNELBUSSESTARGETS)
+all :: all-kernel-busses
+
+#
+# If $MODPREF/kernel exists, we presume the new (2.4.0) /lib/modules/x.y.z directory
+# layout, so we install in kernel/drivers/i2c/busses and remove old versions in misc/
+# and kernel/drivers/i2c/ . Otherwise we install in misc/ as before.
+#
+install-kernel-busses: all-kernel-busses
+	if [ -n "$(KERNELBUSSESTARGETS)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses ; \
+	  $(INSTALL) -m 644 $(KERNELBUSSESTARGETS) $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses ; \
+	  for i in $(KERNELBUSSESTARGETS) ; do \
+	    $(RM) $(DESTDIR)$(MODPREF)/misc/`basename $$i` $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/`basename $$i` \
+	          $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/`basename $$i`.gz $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses/`basename $$i`.gz ; \
+	  done ; \
+	  $(RMDIR) $(DESTDIR)$(MODPREF)/misc 2> /dev/null || true ; \
+	fi
+
+install :: install-kernel-busses
+
+clean-kernel-busses:
+	$(RM) $(KERNELBUSSESDIR)/*.o $(KERNELBUSSESDIR)/*.d
+clean :: clean-kernel-busses
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.h
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.h	(revision 3251)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-keywest.h	(revision 3251)
@@ -0,0 +1,113 @@
+#ifndef __I2C_KEYWEST_H__
+#define __I2C_KEYWEST_H__
+
+#include <asm/semaphore.h>
+
+/* The Tumbler audio equalizer can be really slow sometimes */
+#define POLL_TIMEOUT		(2*HZ)
+
+/* Register indices */
+typedef enum {
+	reg_mode = 0,
+	reg_control,
+	reg_status,
+	reg_isr,
+	reg_ier,
+	reg_addr,
+	reg_subaddr,
+	reg_data
+} reg_t;
+
+
+/* Mode register */
+#define KW_I2C_MODE_100KHZ	0x00
+#define KW_I2C_MODE_50KHZ	0x01
+#define KW_I2C_MODE_25KHZ	0x02
+#define KW_I2C_MODE_DUMB	0x00
+#define KW_I2C_MODE_STANDARD	0x04
+#define KW_I2C_MODE_STANDARDSUB	0x08
+#define KW_I2C_MODE_COMBINED	0x0C
+#define KW_I2C_MODE_MODE_MASK	0x0C
+#define KW_I2C_MODE_CHAN_MASK	0xF0
+
+/* Control register */
+#define KW_I2C_CTL_AAK		0x01
+#define KW_I2C_CTL_XADDR	0x02
+#define KW_I2C_CTL_STOP		0x04
+#define KW_I2C_CTL_START	0x08
+
+/* Status register */
+#define KW_I2C_STAT_BUSY	0x01
+#define KW_I2C_STAT_LAST_AAK	0x02
+#define KW_I2C_STAT_LAST_RW	0x04
+#define KW_I2C_STAT_SDA		0x08
+#define KW_I2C_STAT_SCL		0x10
+
+/* IER & ISR registers */
+#define KW_I2C_IRQ_DATA		0x01
+#define KW_I2C_IRQ_ADDR		0x02
+#define KW_I2C_IRQ_STOP		0x04
+#define KW_I2C_IRQ_START	0x08
+#define KW_I2C_IRQ_MASK		0x0F
+
+/* Physical interface */
+struct keywest_iface
+{
+	unsigned long		base;
+	unsigned		bsteps;
+	int			irq;
+	struct semaphore	sem;
+	spinlock_t		lock;
+	struct keywest_chan*	channels;
+	unsigned		chan_count;
+	u8			cur_mode;
+	char			read_write;
+	u8*			data;
+	unsigned		datalen;
+	int			state;
+	int			result;
+	int			stopretry;
+	struct timer_list	timeout_timer;
+	struct completion	complete;
+	struct keywest_iface*	next;
+};
+
+enum {
+	state_idle,
+	state_addr,
+	state_read,
+	state_write,
+	state_stop,
+	state_dead
+};
+
+/* Channel on an interface */
+struct keywest_chan
+{
+	struct i2c_adapter	adapter;
+	struct keywest_iface*	iface;
+	unsigned		chan_no;
+};
+
+/* Register access */
+
+static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
+{
+	return in_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps));
+}
+
+static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
+{
+	out_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps), val);
+	(void)__read_reg(iface, reg);
+	udelay(10);
+}
+
+#define write_reg(reg, val)	__write_reg(iface, reg, val) 
+#define read_reg(reg)		__read_reg(iface, reg) 
+
+
+
+#endif /* __I2C_KEYWEST_H__ */
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-viapro.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-viapro.c	(revision 4039)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-viapro.c	(revision 4039)
@@ -0,0 +1,525 @@
+/*
+    i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>,
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    Copyright (C) 2005  Jean Delvare <khali@linux-fr.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.
+*/
+
+/*
+   Supports the following VIA south bridges:
+
+   Chip name          PCI ID  REV     I2C block
+   VT82C596A          0x3050             no
+   VT82C596B          0x3051             no
+   VT82C686A          0x3057  0x30       no
+   VT82C686B          0x3057  0x40       yes
+   VT8231             0x8235             no?
+   VT8233             0x3074             yes
+   VT8233A            0x3147             yes?
+   VT8235             0x3177             yes
+   VT8237R            0x3227             yes
+
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+/* 8233A is undefined before kernel 2.4.19 */
+#ifndef PCI_DEVICE_ID_VIA_8233A
+#define PCI_DEVICE_ID_VIA_8233A	0x3147
+#endif
+/* 8235 is undefined before kernel 2.4.21 */
+#ifndef PCI_DEVICE_ID_VIA_8235
+#define PCI_DEVICE_ID_VIA_8235	0x3177
+#endif
+/* 8237 is undefined before kernel 2.4.21 */
+#ifndef PCI_DEVICE_ID_VIA_8237
+#define PCI_DEVICE_ID_VIA_8237	0x3227
+#endif
+
+#define SMBBA1		0x90
+#define SMBBA2		0x80
+#define SMBBA3		0xD0
+
+/* SMBus address offsets */
+static unsigned short vt596_smba;
+#define SMBHSTSTS	(vt596_smba + 0)
+#define SMBHSTCNT	(vt596_smba + 2)
+#define SMBHSTCMD	(vt596_smba + 3)
+#define SMBHSTADD	(vt596_smba + 4)
+#define SMBHSTDAT0	(vt596_smba + 5)
+#define SMBHSTDAT1	(vt596_smba + 6)
+#define SMBBLKDAT	(vt596_smba + 7)
+
+/* PCI Address Constants */
+
+/* SMBus data in configuration space can be found in two places,
+   We try to select the better one */
+
+static unsigned short SMBHSTCFG = 0xD2;
+
+/* Other settings */
+#define MAX_TIMEOUT	500
+
+/* VT82C596 constants */
+#define VT596_QUICK		0x00
+#define VT596_BYTE		0x04
+#define VT596_BYTE_DATA		0x08
+#define VT596_WORD_DATA		0x0C
+#define VT596_BLOCK_DATA	0x14
+#define VT596_I2C_BLOCK_DATA	0x34
+
+
+/* If force is set to anything different from 0, we forcibly enable the
+   VT596. DANGEROUS! */
+static int force;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the VT596 at the given address. VERY DANGEROUS! */
+static int force_addr;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the SMBus at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+
+static struct i2c_adapter vt596_adapter;
+
+#define FEATURE_I2CBLOCK	(1<<0)
+static unsigned int vt596_features;
+
+#ifdef DEBUG
+static void vt596_dump_regs(const char *msg, u8 size)
+{
+	dev_dbg(&vt596_adapter, "%s: STS=%02x CNT=%02x CMD=%02x ADD=%02x "
+		"DAT=%02x,%02x\n", msg, inb_p(SMBHSTSTS), inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		inb_p(SMBHSTDAT1));
+
+	if (size == VT596_BLOCK_DATA
+	 || size == VT596_I2C_BLOCK_DATA) {
+		int i;
+
+		dev_dbg(&vt596_adapter, "BLK=");
+		for (i = 0; i < I2C_SMBUS_BLOCK_MAX / 2; i++)
+			printk("%02x,", inb_p(SMBBLKDAT));
+		printk("\n");
+		dev_dbg(&vt596_adapter, "    ");
+		for (; i < I2C_SMBUS_BLOCK_MAX - 1; i++)
+			printk("%02x,", inb_p(SMBBLKDAT));
+		printk("%02x\n", inb_p(SMBBLKDAT));
+	}
+}
+#else
+static inline void vt596_dump_regs(const char *msg, u8 size) { }
+#endif
+
+/* Return -1 on error, 0 on success */
+static int vt596_transaction(u8 size)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	vt596_dump_regs("Transaction (pre)", size);
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
+		dev_dbg(&vt596_adapter, "SMBus busy (0x%02x). "
+			"Resetting...\n", temp);
+
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
+			dev_err(&vt596_adapter, "SMBus reset failed! "
+				"(0x%02x)\n", temp);
+			return -1;
+		}
+	}
+
+	/* Start the transaction by setting bit 6 */
+	outb_p(0x40 | size, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		dev_err(&vt596_adapter, "SMBus Timeout!\n");
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+		dev_err(&vt596_adapter, "Transaction failed (0x%02x)\n", size);
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		dev_err(&vt596_adapter, "SMBus collision!\n");
+	}
+
+	if (temp & 0x04) {
+		int read = inb_p(SMBHSTADD) & 0x01;
+		result = -1;
+		/* The quick and receive byte commands are used to probe
+		   for chips, so errors are expected, and we don't want
+		   to frighten the user. */
+		if (!((size == VT596_QUICK && !read) ||
+		      (size == VT596_BYTE && read)))
+			dev_err(&vt596_adapter, "Transaction error!\n");
+	}
+
+	/* Resetting status register */
+	if (temp & 0x1F)
+		outb_p(temp, SMBHSTSTS);
+
+	vt596_dump_regs("Transaction (post)", size);
+
+	return result;
+}
+
+/* Return -1 on error, 0 on success */
+static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
+		unsigned short flags, char read_write, u8 command,
+		int size, union i2c_smbus_data *data)
+{
+	int i;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		size = VT596_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = VT596_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = VT596_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = VT596_WORD_DATA;
+		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		if (!(vt596_features & FEATURE_I2CBLOCK))
+			goto exit_unsupported;
+		if (read_write == I2C_SMBUS_READ)
+			outb_p(I2C_SMBUS_BLOCK_MAX, SMBHSTDAT0);
+		/* Fall through */
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			u8 len = data->block[0];
+			if (len > I2C_SMBUS_BLOCK_MAX)
+				len = I2C_SMBUS_BLOCK_MAX;
+			outb_p(len, SMBHSTDAT0);
+			inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = (size == I2C_SMBUS_I2C_BLOCK_DATA) ?
+		       VT596_I2C_BLOCK_DATA : VT596_BLOCK_DATA;
+		break;
+	default:
+		goto exit_unsupported;
+	}
+
+	outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD);
+
+	if (vt596_transaction(size)) /* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
+		return 0;
+
+	switch (size) {
+	case VT596_BYTE:
+	case VT596_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case VT596_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case VT596_I2C_BLOCK_DATA:
+	case VT596_BLOCK_DATA:
+		data->block[0] = inb_p(SMBHSTDAT0);
+		if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
+			data->block[0] = I2C_SMBUS_BLOCK_MAX;
+		inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMBBLKDAT);
+		break;
+	}
+	return 0;
+
+exit_unsupported:
+	dev_warn(&vt596_adapter, "Unsupported command invoked! (0x%02x)\n",
+		 size);
+	return -1;
+}
+
+static void vt596_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void vt596_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static u32 vt596_func(struct i2c_adapter *adapter)
+{
+	u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+
+	if (vt596_features & FEATURE_I2CBLOCK)
+		func |= I2C_FUNC_SMBUS_I2C_BLOCK;
+	return func;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= vt596_access,
+	.functionality	= vt596_func,
+};
+
+static struct i2c_adapter vt596_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2,
+	.algo		= &smbus_algorithm,
+	.inc_use	= vt596_inc,
+	.dec_use	= vt596_dec,
+};
+
+static int __init vt596_probe(struct pci_dev *pdev,
+			      const struct pci_device_id *id)
+{
+	unsigned char temp;
+	int error = -ENODEV;
+
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		vt596_smba = force_addr & 0xfff0;
+		force = 0;
+		goto found;
+	}
+
+	if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) ||
+	    !(vt596_smba & 0x0001)) {
+		/* try 2nd address and config reg. for 596 */
+		if (id->device == PCI_DEVICE_ID_VIA_82C596_3 &&
+		    !pci_read_config_word(pdev, SMBBA2, &vt596_smba) &&
+		    (vt596_smba & 0x0001)) {
+			SMBHSTCFG = 0x84;
+		} else {
+			/* no matches at all */
+			dev_err(pdev, "Cannot configure "
+				"SMBus I/O Base address\n");
+			return -ENODEV;
+		}
+	}
+
+	vt596_smba &= 0xfff0;
+	if (vt596_smba == 0) {
+		dev_err(pdev, "SMBus base address "
+			"uninitialized - upgrade BIOS or use "
+			"force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+found:
+	if (!request_region(vt596_smba, 8, "viapro-smbus")) {
+		dev_err(pdev, "SMBus region 0x%x already in use!\n",
+		        vt596_smba);
+		return -ENODEV;
+	}
+
+	pci_read_config_byte(pdev, SMBHSTCFG, &temp);
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the VT596 first. */
+	if (force_addr) {
+		pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(pdev, id->driver_data, vt596_smba);
+		pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
+		dev_warn(pdev, "WARNING: SMBus interface set to new "
+			 "address 0x%04x!\n", vt596_smba);
+	} else if (!(temp & 0x01)) {
+		if (force) {
+			/* NOTE: This assumes I/O space and other allocations
+			 * WERE done by the Bios!  Don't complain if your
+			 * hardware does weird things after enabling this.
+			 * :') Check for Bios updates before resorting to
+			 * this.
+			 */
+			pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
+			dev_info(pdev, "Enabling SMBus device\n");
+		} else {
+			dev_err(pdev, "SMBUS: Error: Host SMBus "
+				"controller not enabled! - upgrade BIOS or "
+				"use force=1\n");
+			goto release_region;
+		}
+	}
+
+	dev_dbg(pdev, "VT596_smba = 0x%X\n", vt596_smba);
+
+	switch (id->device) {
+	case PCI_DEVICE_ID_VIA_8237:
+	case PCI_DEVICE_ID_VIA_8235:
+	case PCI_DEVICE_ID_VIA_8233A:
+	case PCI_DEVICE_ID_VIA_8233_0:
+		vt596_features |= FEATURE_I2CBLOCK;
+		break;
+	case PCI_DEVICE_ID_VIA_82C686_4:
+		/* The VT82C686B (rev 0x40) does support I2C block
+		   transactions, but the VT82C686A (rev 0x30) doesn't */
+		if (!pci_read_config_byte(pdev, PCI_REVISION_ID, &temp)
+		 && temp >= 0x40)
+			vt596_features |= FEATURE_I2CBLOCK;
+		break;
+	}
+
+	snprintf(vt596_adapter.name, 32,
+		 "SMBus Via Pro adapter at %04x", vt596_smba);
+
+	return i2c_add_adapter(&vt596_adapter);
+
+release_region:
+	release_region(vt596_smba, 8);
+	return error;
+}
+
+static struct pci_device_id vt596_ids[] __initdata = {
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C596_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device		= PCI_DEVICE_ID_VIA_82C596B_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C686_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233_0,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233A,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8235,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8237,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8231_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{ 0, }
+};
+
+static int __init i2c_vt596_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	pci_for_each_dev(dev) {
+		id = pci_match_device(vt596_ids, dev);
+		if(id)
+			if(vt596_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_vt596_exit(void)
+{
+	i2c_del_adapter(&vt596_adapter);
+	release_region(vt596_smba, 8);
+}
+
+MODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>, "
+	      "Mark D. Studebaker <mdsxyz123@yahoo.com> and "
+	      "Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("vt82c596 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_vt596_init);
+module_exit(i2c_vt596_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/dmi_scan.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/dmi_scan.c	(revision 2423)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/dmi_scan.c	(revision 2423)
@@ -0,0 +1,249 @@
+/*
+	Taken from arch/i386/kernel/dmi_scan.c.
+	Changes dmi_ident to be non-static so we can access.
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/apm_bios.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/keyboard.h>
+#include <asm/system.h>
+#include "version.h"
+
+int is_unsafe_smbus;
+EXPORT_SYMBOL(is_unsafe_smbus);
+
+struct dmi_header
+{
+	u8	type;
+	u8	length;
+	u16	handle;
+};
+
+enum
+{
+	DMI_BIOS_VENDOR,
+	DMI_BIOS_VERSION,
+	DMI_BIOS_DATE,
+	DMI_SYS_VENDOR,
+	DMI_PRODUCT_NAME,
+	DMI_PRODUCT_VERSION,
+	DMI_BOARD_VENDOR,
+	DMI_BOARD_NAME,
+	DMI_BOARD_VERSION,
+	DMI_STRING_MAX
+};
+
+#define dmi_printk(x)
+//#define dmi_printk(x) printk x
+
+static char * __init dmi_string(struct dmi_header *dm, u8 s)
+{
+	u8 *bp=(u8 *)dm;
+	bp+=dm->length;
+	if(!s)
+		return "";
+	s--;
+	while(s>0 && *bp)
+	{
+		bp+=strlen(bp);
+		bp++;
+		s--;
+	}
+	return bp;
+}
+
+/*
+ *	We have to be cautious here. We have seen BIOSes with DMI pointers
+ *	pointing to completely the wrong place for example
+ */
+ 
+static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *))
+{
+	u8 *buf;
+	struct dmi_header *dm;
+	u8 *data;
+	int i=0;
+		
+	buf = ioremap(base, len);
+	if(buf==NULL)
+		return -1;
+
+	data = buf;
+
+	/*
+ 	 *	Stop when we see all the items the table claimed to have
+ 	 *	OR we run off the end of the table (also happens)
+ 	 */
+ 
+	while(i<num && data-buf+sizeof(struct dmi_header)<=len)
+	{
+		dm=(struct dmi_header *)data;
+		/*
+		 *  We want to know the total length (formated area and strings)
+		 *  before decoding to make sure we won't run off the table in
+		 *  dmi_decode or dmi_string
+		 */
+		data+=dm->length;
+		while(data-buf<len-1 && (data[0] || data[1]))
+			data++;
+		if(data-buf<len-1)
+			decode(dm);
+		data+=2;
+		i++;
+	}
+	iounmap(buf);
+	return 0;
+}
+
+
+inline static int __init dmi_checksum(u8 *buf)
+{
+	u8 sum=0;
+	int a;
+	
+	for(a=0; a<15; a++)
+		sum+=buf[a];
+	return (sum==0);
+}
+
+static int __init dmi_iterate(void (*decode)(struct dmi_header *))
+{
+	u8 buf[15];
+	u32 fp=0xF0000;
+
+#ifdef CONFIG_SIMNOW
+	/*
+ 	 *	Skip on x86/64 with simnow. Will eventually go away
+ 	 *	If you see this ifdef in 2.6pre mail me !
+ 	 */
+	return -1;
+#endif
+ 	
+	while( fp < 0xFFFFF)
+	{
+		isa_memcpy_fromio(buf, fp, 15);
+		if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf))
+		{
+			u16 num=buf[13]<<8|buf[12];
+			u16 len=buf[7]<<8|buf[6];
+			u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8];
+
+			/*
+			 * DMI version 0.0 means that the real version is taken from
+			 * the SMBIOS version, which we don't know at this point.
+			 */
+			if(buf[14]!=0)
+				dmi_printk((KERN_INFO "DMI %d.%d present.\n",
+					buf[14]>>4, buf[14]&0x0F));
+			else
+				dmi_printk((KERN_INFO "DMI present.\n"));
+			dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n",
+				num, len));
+			dmi_printk((KERN_INFO "DMI table at 0x%08X.\n",
+				base));
+			if(dmi_table(base,len, num, decode)==0)
+				return 0;
+		}
+		fp+=16;
+	}
+	return -1;
+}
+
+
+char *dmi_ident[DMI_STRING_MAX];
+
+/*
+ *	Save a DMI string
+ */
+ 
+static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
+{
+	char *d = (char*)dm;
+	char *p = dmi_string(dm, d[string]);
+	if(p==NULL || *p == 0)
+		return;
+	if (dmi_ident[slot])
+		return;
+	dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
+	if(dmi_ident[slot])
+		strcpy(dmi_ident[slot], p);
+	else
+		printk(KERN_ERR "dmi_save_ident: out of memory.\n");
+}
+
+/*
+ *	Process a DMI table entry. Right now all we care about are the BIOS
+ *	and machine entries. For 2.5 we should pull the smbus controller info
+ *	out of here.
+ */
+
+static void __init dmi_decode(struct dmi_header *dm)
+{
+	switch(dm->type)
+	{
+		case  0:
+			dmi_printk(("BIOS Vendor: %s\n",
+				dmi_string(dm, data[4])));
+			dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
+			dmi_printk(("BIOS Version: %s\n", 
+				dmi_string(dm, data[5])));
+			dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
+			dmi_printk(("BIOS Release: %s\n",
+				dmi_string(dm, data[8])));
+			dmi_save_ident(dm, DMI_BIOS_DATE, 8);
+			break;
+		case 1:
+			dmi_printk(("System Vendor: %s\n",
+				dmi_string(dm, data[4])));
+			dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
+			dmi_printk(("Product Name: %s\n",
+				dmi_string(dm, data[5])));
+			dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
+			dmi_printk(("Version: %s\n",
+				dmi_string(dm, data[6])));
+			dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
+			dmi_printk(("Serial Number: %s\n",
+				dmi_string(dm, data[7])));
+			break;
+		case 2:
+			dmi_printk(("Board Vendor: %s\n",
+				dmi_string(dm, data[4])));
+			dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
+			dmi_printk(("Board Name: %s\n",
+				dmi_string(dm, data[5])));
+			dmi_save_ident(dm, DMI_BOARD_NAME, 5);
+			dmi_printk(("Board Version: %s\n",
+				dmi_string(dm, data[6])));
+			dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
+			break;
+	}
+}
+
+int __init dmi_scan_mach(void)
+{
+	int err;
+	printk("dmi_scan.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	err = dmi_iterate(dmi_decode);
+	if(err)
+		printk("dmi_scan.o: SM BIOS not found\n");
+	else
+		printk("dmi_scan.o: SM BIOS found\n");
+
+	if(dmi_ident[DMI_SYS_VENDOR] != NULL
+	&& strncmp(dmi_ident[DMI_SYS_VENDOR], "IBM", 3) == 0)
+		is_unsafe_smbus = 1;
+	return 0;
+}
+
+MODULE_DESCRIPTION("SM BIOS DMI Scanner");
+MODULE_LICENSE("GPL");
+
+module_init(dmi_scan_mach);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756.c	(revision 4057)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd756.c	(revision 4057)
@@ -0,0 +1,444 @@
+/*
+    amd756.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
+
+    Shamelessly ripped from i2c-piix4.c:
+
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.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.
+*/
+
+/*
+    2002-04-08: Added nForce support. (Csaba Halasz)
+    2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
+    2002-12-28: Rewritten into something that resembles a Linux driver (hch)
+    2003-11-29: Added back AMD8111 removed by the previous rewrite.
+                (Philip Pokorny)
+    2004-02-15: Don't register driver to avoid driver conflicts.
+                (Daniel Rune Jensen)
+*/
+
+/*
+   Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+
+#define DRV_NAME	"i2c-amd756"
+
+/* AMD756 SMBus address offsets */
+#define SMB_ADDR_OFFSET        0xE0
+#define SMB_IOSIZE             16
+#define SMB_GLOBAL_STATUS      (0x0 + amd756_ioport)
+#define SMB_GLOBAL_ENABLE      (0x2 + amd756_ioport)
+#define SMB_HOST_ADDRESS       (0x4 + amd756_ioport)
+#define SMB_HOST_DATA          (0x6 + amd756_ioport)
+#define SMB_HOST_COMMAND       (0x8 + amd756_ioport)
+#define SMB_HOST_BLOCK_DATA    (0x9 + amd756_ioport)
+#define SMB_HAS_DATA           (0xA + amd756_ioport)
+#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport)
+#define SMB_HAS_HOST_ADDRESS   (0xE + amd756_ioport)
+#define SMB_SNOOP_ADDRESS      (0xF + amd756_ioport)
+
+/* PCI Address Constants */
+
+/* address of I/O space */
+#define SMBBA     0x058		/* mh */
+#define SMBBANFORCE     0x014
+
+/* general configuration */
+#define SMBGCFG   0x041		/* mh */
+
+/* silicon revision code */
+#define SMBREV    0x008
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* AMD756 constants */
+#define AMD756_QUICK        0x00
+#define AMD756_BYTE         0x01
+#define AMD756_BYTE_DATA    0x02
+#define AMD756_WORD_DATA    0x03
+#define AMD756_PROCESS_CALL 0x04
+#define AMD756_BLOCK_DATA   0x05
+
+
+static unsigned short amd756_ioport = 0;
+
+/* 
+  SMBUS event = I/O 28-29 bit 11
+     see E0 for the status bits and enabled in E2
+     
+*/
+
+#define GS_ABRT_STS (1 << 0)
+#define GS_COL_STS (1 << 1)
+#define GS_PRERR_STS (1 << 2)
+#define GS_HST_STS (1 << 3)
+#define GS_HCYC_STS (1 << 4)
+#define GS_TO_STS (1 << 5)
+#define GS_SMB_STS (1 << 11)
+
+#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
+  GS_HCYC_STS | GS_TO_STS )
+
+#define GE_CYC_TYPE_MASK (7)
+#define GE_HOST_STC (1 << 3)
+#define GE_ABORT (1 << 5)
+
+
+static int amd756_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	pr_debug(DRV_NAME
+	       ": Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
+	       inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
+	       inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
+		pr_debug(DRV_NAME ": SMBus busy (%04x). Waiting...\n", temp);
+		do {
+			i2c_delay(1);
+			temp = inw_p(SMB_GLOBAL_STATUS);
+		} while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
+		         (timeout++ < MAX_TIMEOUT));
+		/* If the SMBus is still busy, we give up */
+		if (timeout >= MAX_TIMEOUT) {
+			pr_debug(DRV_NAME ": Busy wait timeout (%04x)\n", temp);
+			goto abort;
+		}
+		timeout = 0;
+	}
+
+	/* start the transaction by setting the start bit */
+	outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = inw_p(SMB_GLOBAL_STATUS);
+	} while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		pr_debug(DRV_NAME ": Completion timeout!\n");
+		goto abort;
+	}
+
+	if (temp & GS_PRERR_STS) {
+		result = -1;
+		pr_debug(DRV_NAME ": SMBus Protocol error (no response)!\n");
+	}
+
+	if (temp & GS_COL_STS) {
+		result = -1;
+		printk(KERN_WARNING DRV_NAME ": SMBus collision!\n");
+	}
+
+	if (temp & GS_TO_STS) {
+		result = -1;
+		pr_debug(DRV_NAME ": SMBus protocol timeout!\n");
+	}
+
+	if (temp & GS_HCYC_STS)
+		pr_debug(DRV_NAME ": SMBus protocol success!\n");
+
+	outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
+
+#ifdef DEBUG
+	if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
+		pr_debug(DRV_NAME
+		         ": Failed reset at end of transaction (%04x)\n", temp);
+	}
+
+	pr_debug(DRV_NAME
+		 ": Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
+		 inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
+		 inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
+#endif
+
+	return result;
+
+ abort:
+	printk(KERN_WARNING DRV_NAME ": Sending abort.\n");
+	outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
+	i2c_delay(100);
+	outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
+	return -1;
+}
+
+/* Return -1 on error. */
+
+static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
+		  unsigned short flags, char read_write,
+		  u8 command, int size, union i2c_smbus_data * data)
+{
+	int i, len;
+
+	/** TODO: Should I supporte the 10-bit transfers? */
+	switch (size) {
+	/* TODO: proc call is supported, I'm just not sure what to do here... */
+	case I2C_SMBUS_QUICK:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		size = AMD756_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMB_HOST_DATA);
+		size = AMD756_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE)
+			outw_p(data->byte, SMB_HOST_DATA);
+		size = AMD756_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE)
+			outw_p(data->word, SMB_HOST_DATA);	/* TODO: endian???? */
+		size = AMD756_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outw_p(len, SMB_HOST_DATA);
+			/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i],
+				       SMB_HOST_BLOCK_DATA);
+		}
+		size = AMD756_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-amd756.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	/* How about enabling interrupts... */
+	outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
+
+	if (amd756_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case AMD756_BYTE:
+		data->byte = inw_p(SMB_HOST_DATA);
+		break;
+	case AMD756_BYTE_DATA:
+		data->byte = inw_p(SMB_HOST_DATA);
+		break;
+	case AMD756_WORD_DATA:
+		data->word = inw_p(SMB_HOST_DATA);	/* TODO: endian???? */
+		break;
+	case AMD756_BLOCK_DATA:
+		data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
+		if(data->block[0] > 32)
+			data->block[0] = 32;
+		/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
+		break;
+	}
+
+	return 0;
+}
+
+static void amd756_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void amd756_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static u32 amd756_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= amd756_access,
+	.functionality	= amd756_func,
+};
+
+struct i2c_adapter amd756_smbus = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
+	.algo		= &smbus_algorithm,
+	.inc_use	= amd756_inc,
+	.dec_use	= amd756_dec,
+};
+
+enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
+static const char* chipname[] = {
+	"AMD756", "AMD766", "AMD768",
+	"nVidia nForce", "AMD8111",
+};
+
+static struct pci_device_id amd756_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 },
+	{PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 },
+	{PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 },
+	{PCI_VENDOR_ID_AMD, 0x746B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111 },
+	{PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
+	{ 0, }
+};
+
+static int __devinit amd756_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *id)
+{
+	int nforce = (id->driver_data == NFORCE);
+	int error;
+	u8 temp;
+	
+	if (amd756_ioport) {
+		printk(KERN_ERR DRV_NAME ": Only one device supported. "
+		       "(you have a strange motherboard, btw..)\n");
+		return -ENODEV;
+	}
+
+	if (nforce) {
+		if (PCI_FUNC(pdev->devfn) != 1)
+			return -ENODEV;
+
+		pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport);
+		amd756_ioport &= 0xfffc;
+	} else { /* amd */
+		if (PCI_FUNC(pdev->devfn) != 3)
+			return -ENODEV;
+
+		pci_read_config_byte(pdev, SMBGCFG, &temp);
+		if ((temp & 128) == 0) {
+			printk(KERN_ERR DRV_NAME
+			       ": Error: SMBus controller I/O not enabled!\n");
+			return -ENODEV;
+		}
+
+		/* Determine the address of the SMBus areas */
+		/* Technically it is a dword but... */
+		pci_read_config_word(pdev, SMBBA, &amd756_ioport);
+		amd756_ioport &= 0xff00;
+		amd756_ioport += SMB_ADDR_OFFSET;
+	}
+
+	if (!request_region(amd756_ioport, SMB_IOSIZE, "amd756-smbus")) {
+		printk(KERN_ERR DRV_NAME
+		       ": SMB region 0x%x already in use!\n", amd756_ioport);
+		return -ENODEV;
+	}
+
+#ifdef DEBUG
+	pci_read_config_byte(pdev, SMBREV, &temp);
+	printk(KERN_DEBUG DRV_NAME ": SMBREV = 0x%X\n", temp);
+	printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport);
+#endif
+
+	sprintf(amd756_smbus.name, "SMBus %s adapter at %04x",
+		chipname[id->driver_data], amd756_ioport);
+
+	error = i2c_add_adapter(&amd756_smbus);
+	if (error) {
+		printk(KERN_ERR DRV_NAME
+		       ": Adapter registration failed, module not inserted.\n");
+		goto out_err;
+	}
+
+	return 0;
+
+ out_err:
+	release_region(amd756_ioport, SMB_IOSIZE);
+	return error;
+}
+
+
+static int __init i2c_amd756_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk(KERN_INFO "i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+ 	pci_for_each_dev(dev) {
+		id = pci_match_device(amd756_ids, dev);
+		if (id && amd756_probe(dev, id) >= 0)
+			return 0; 
+	}
+
+	return -ENODEV;
+}
+
+
+static void __exit i2c_amd756_exit(void)
+{
+	i2c_del_adapter(&amd756_smbus);
+	release_region(amd756_ioport, SMB_IOSIZE);
+}
+
+MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
+MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(amd756_smbus);
+
+module_init(i2c_amd756_init)
+module_exit(i2c_amd756_exit)
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-hydra.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-hydra.c	(revision 2802)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-hydra.c	(revision 2802)
@@ -0,0 +1,191 @@
+/*
+    i2c-hydra.c - Part of lm_sensors,  Linux kernel modules
+                  for hardware monitoring
+
+    i2c Support for the Apple `Hydra' Mac I/O
+
+    Copyright (c) 1999 Geert Uytterhoeven <geert@linux-m68k.org>
+
+    Based on i2c Support for Via Technologies 82C586B South Bridge
+    Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/param.h>	/* for HZ */
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+
+
+#define HYDRA_CACHE_PD	0x00000030
+
+#define HYDRA_CPD_PD0	0x00000001	/* CachePD lines */
+#define HYDRA_CPD_PD1	0x00000002
+#define HYDRA_CPD_PD2	0x00000004
+#define HYDRA_CPD_PD3	0x00000008
+
+#define HYDRA_SCLK	HYDRA_CPD_PD0
+#define HYDRA_SDAT	HYDRA_CPD_PD1
+#define HYDRA_SCLK_OE	0x00000010
+#define HYDRA_SDAT_OE	0x00000020
+
+static unsigned long hydra_base;
+
+static inline void pdregw(u32 val)
+{
+	writel(val, hydra_base + HYDRA_CACHE_PD);
+}
+
+static inline u32 pdregr(void)
+{
+	u32 val = readl(hydra_base + HYDRA_CACHE_PD);
+	return val;
+}
+
+static void bit_hydra_setscl(void *data, int state)
+{
+	u32 val = pdregr();
+	if (state)
+		val &= ~HYDRA_SCLK_OE;
+	else {
+		val &= ~HYDRA_SCLK;
+		val |= HYDRA_SCLK_OE;
+	}
+	pdregw(val);
+	pdregr();	/* flush posted write */
+}
+
+static void bit_hydra_setsda(void *data, int state)
+{
+	u32 val = pdregr();
+	if (state)
+		val &= ~HYDRA_SDAT_OE;
+	else {
+		val &= ~HYDRA_SDAT;
+		val |= HYDRA_SDAT_OE;
+	}
+	pdregw(val);
+	pdregr();	/* flush posted write */
+}
+
+static int bit_hydra_getscl(void *data)
+{
+	return (pdregr() & HYDRA_SCLK) != 0;
+}
+
+static int bit_hydra_getsda(void *data)
+{
+	return (pdregr() & HYDRA_SDAT) != 0;
+}
+
+static void bit_hydra_inc(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void bit_hydra_dec(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct i2c_algo_bit_data bit_hydra_data = {
+	.setsda		= bit_hydra_setsda,
+	.setscl		= bit_hydra_setscl,
+	.getsda		= bit_hydra_getsda,
+	.getscl		= bit_hydra_getscl,
+	.udelay		= 5,
+	.mdelay		= 5,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter bit_hydra_ops = {
+	.name		= "Hydra i2c",
+	.id		= I2C_HW_B_HYDRA,
+	.algo_data	= &bit_hydra_data,
+	.inc_use	= bit_hydra_inc,
+	.dec_use	= bit_hydra_dec,
+};
+
+static struct pci_device_id hydra_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_APPLE,
+		.device =	PCI_DEVICE_ID_APPLE_HYDRA,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit hydra_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	unsigned int base_addr;
+
+	base_addr = dev->resource[0].start;
+	hydra_base = (unsigned long) ioremap(base_addr, 0x100);
+
+	pdregw(0);		/* clear SCLK_OE and SDAT_OE */
+ 	return i2c_bit_add_bus(&bit_hydra_ops);
+}
+
+static void __devexit hydra_remove(struct pci_dev *dev)
+{
+	pdregw(0);	/* clear SCLK_OE and SDAT_OE */
+	i2c_bit_del_bus(&bit_hydra_ops);
+	iounmap((void *) hydra_base);
+}
+
+
+static struct pci_driver hydra_driver = {
+	.name		= "hydra smbus",
+	.id_table	= hydra_ids,
+	.probe		= hydra_probe,
+	.remove		= __devexit_p(hydra_remove),
+};
+
+static int __init i2c_hydra_init(void)
+{
+	return pci_module_init(&hydra_driver);
+}
+
+
+static void __exit i2c_hydra_exit(void)
+{
+	pci_unregister_driver(&hydra_driver);
+}
+
+
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
+
+module_init(i2c_hydra_init);
+module_exit(i2c_hydra_exit);
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-isa.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-isa.c	(revision 2772)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-isa.c	(revision 2772)
@@ -0,0 +1,88 @@
+/*
+    i2c-isa.c - 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.
+*/
+
+/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
+   on first sight very useful; almost no functionality is preserved.
+   Except that it makes writing drivers for chips which can be on both
+   the SMBus and the ISA bus very much easier. See lm78.c for an example
+   of this. */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+static u32 isa_func(struct i2c_adapter *adapter);
+
+/* This is the actual algorithm we define */
+static struct i2c_algorithm isa_algorithm = {
+	.name		= "ISA bus algorithm",
+	.id		= I2C_ALGO_ISA,
+	.functionality	= isa_func,
+};
+
+static void isa_inc_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void isa_dec_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* There can only be one... */
+static struct i2c_adapter isa_adapter = {
+	.name		= "ISA main adapter",
+	.id		= I2C_ALGO_ISA | I2C_HW_ISA,
+	.algo		= &isa_algorithm,
+	.inc_use	= isa_inc_use,
+	.dec_use	= isa_dec_use,
+};
+
+/* We can't do a thing... */
+static u32 isa_func(struct i2c_adapter *adapter)
+{
+	return 0;
+}
+
+static int __init i2c_isa_init(void)
+{
+	printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_adapter(&isa_adapter);
+}
+
+static void __exit i2c_isa_exit(void)
+{
+	i2c_del_adapter(&isa_adapter);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("ISA bus access through i2c");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_isa_init);
+module_exit(i2c_isa_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd8111.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd8111.c	(revision 3177)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-amd8111.c	(revision 3177)
@@ -0,0 +1,424 @@
+/*
+ * SMBus 2.0 driver for AMD-8111 IO-Hub.
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ *
+ * 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 version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+#ifndef I2C_HW_SMBUS_AMD8111
+#error Your i2c is too old - i2c-2.7.0 or greater required!
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");
+
+struct amd_smbus {
+	struct pci_dev *dev;
+	struct i2c_adapter adapter;
+	int base;
+	int size;
+};
+
+static struct pci_driver amd8111_driver;
+
+/*
+ * AMD PCI control registers definitions.
+ */
+
+#define AMD_PCI_MISC	0x48
+
+#define AMD_PCI_MISC_SCI	0x04	/* deliver SCI */
+#define AMD_PCI_MISC_INT	0x02	/* deliver PCI IRQ */
+#define AMD_PCI_MISC_SPEEDUP	0x01	/* 16x clock speedup */
+
+/*
+ * ACPI 2.0 chapter 13 PCI interface definitions.
+ */
+
+#define AMD_EC_DATA	0x00	/* data register */
+#define AMD_EC_SC	0x04	/* status of controller */
+#define AMD_EC_CMD	0x04	/* command register */
+#define AMD_EC_ICR	0x08	/* interrupt control register */
+
+#define AMD_EC_SC_SMI	0x04	/* smi event pending */
+#define AMD_EC_SC_SCI	0x02	/* sci event pending */
+#define AMD_EC_SC_BURST	0x01	/* burst mode enabled */
+#define AMD_EC_SC_CMD	0x08	/* byte in data reg is command */
+#define AMD_EC_SC_IBF	0x02	/* data ready for embedded controller */
+#define AMD_EC_SC_OBF	0x01	/* data ready for host */
+
+#define AMD_EC_CMD_RD	0x80	/* read EC */
+#define AMD_EC_CMD_WR	0x81	/* write EC */
+#define AMD_EC_CMD_BE	0x82	/* enable burst mode */
+#define AMD_EC_CMD_BD	0x83	/* disable burst mode */
+#define AMD_EC_CMD_QR	0x84	/* query EC */
+
+/*
+ * ACPI 2.0 chapter 13 access of registers of the EC
+ */
+
+unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
+{
+	int timeout = 500;
+
+	while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF))
+		udelay(1);
+
+	if (!timeout) {
+		printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
+{
+	int timeout = 500;
+
+	while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF))
+		udelay(1);
+
+	if (!timeout) {
+		printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for OBF to set\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data)
+{
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(address, smbus->base + AMD_EC_DATA);
+
+	if (amd_ec_wait_read(smbus))
+		return -1;
+	*data = inb(smbus->base + AMD_EC_DATA);
+
+	return 0;
+}
+
+unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data)
+{
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(address, smbus->base + AMD_EC_DATA);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(data, smbus->base + AMD_EC_DATA);
+
+	return 0;
+}
+
+/*
+ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
+ */
+
+#define AMD_SMB_PRTCL	0x00	/* protocol, PEC */
+#define AMD_SMB_STS	0x01	/* status */
+#define AMD_SMB_ADDR	0x02	/* address */
+#define AMD_SMB_CMD	0x03	/* command */
+#define AMD_SMB_DATA	0x04	/* 32 data registers */
+#define AMD_SMB_BCNT	0x24	/* number of data bytes */
+#define AMD_SMB_ALRM_A	0x25	/* alarm address */
+#define AMD_SMB_ALRM_D	0x26	/* 2 bytes alarm data */
+
+#define AMD_SMB_STS_DONE	0x80
+#define AMD_SMB_STS_ALRM	0x40
+#define AMD_SMB_STS_RES		0x20
+#define AMD_SMB_STS_STATUS	0x1f
+
+#define AMD_SMB_STATUS_OK	0x00
+#define AMD_SMB_STATUS_FAIL	0x07
+#define AMD_SMB_STATUS_DNAK	0x10
+#define AMD_SMB_STATUS_DERR	0x11
+#define AMD_SMB_STATUS_CMD_DENY	0x12
+#define AMD_SMB_STATUS_UNKNOWN	0x13
+#define AMD_SMB_STATUS_ACC_DENY	0x17
+#define AMD_SMB_STATUS_TIMEOUT	0x18
+#define AMD_SMB_STATUS_NOTSUP	0x19
+#define AMD_SMB_STATUS_BUSY	0x1A
+#define AMD_SMB_STATUS_PEC	0x1F
+
+#define AMD_SMB_PRTCL_WRITE		0x00
+#define AMD_SMB_PRTCL_READ		0x01
+#define AMD_SMB_PRTCL_QUICK		0x02
+#define AMD_SMB_PRTCL_BYTE		0x04
+#define AMD_SMB_PRTCL_BYTE_DATA		0x06
+#define AMD_SMB_PRTCL_WORD_DATA		0x08
+#define AMD_SMB_PRTCL_BLOCK_DATA	0x0a
+#define AMD_SMB_PRTCL_PROC_CALL		0x0c
+#define AMD_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
+#define AMD_SMB_PRTCL_I2C_BLOCK_DATA	0x4a
+#define AMD_SMB_PRTCL_PEC		0x80
+
+
+s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+		char read_write, u8 command, int size, union i2c_smbus_data * data)
+{
+	struct amd_smbus *smbus = adap->algo_data;
+	unsigned char protocol, len, pec, temp[2];
+	int i;
+
+	protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE;
+	pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_QUICK:
+			protocol |= AMD_SMB_PRTCL_QUICK;
+			read_write = I2C_SMBUS_WRITE;
+			break;
+
+		case I2C_SMBUS_BYTE:
+			if (read_write == I2C_SMBUS_WRITE)
+				amd_ec_write(smbus, AMD_SMB_CMD, command);
+			protocol |= AMD_SMB_PRTCL_BYTE;
+			break;
+
+		case I2C_SMBUS_BYTE_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE)
+				amd_ec_write(smbus, AMD_SMB_DATA, data->byte);
+			protocol |= AMD_SMB_PRTCL_BYTE_DATA;
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				amd_ec_write(smbus, AMD_SMB_DATA, data->word);
+				amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
+			}
+			protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				len = min_t(u8, data->block[0], 32);
+				amd_ec_write(smbus, AMD_SMB_BCNT, len);
+				for (i = 0; i < len; i++)
+					amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			}
+			protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
+			break;
+
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			len = min_t(u8, data->block[0], 32);
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_BCNT, len);
+			if (read_write == I2C_SMBUS_WRITE)
+				for (i = 0; i < len; i++)
+					amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
+			break;
+
+		case I2C_SMBUS_PROC_CALL:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_DATA, data->word);
+			amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
+			protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			len = min_t(u8, data->block[0], 31);
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_BCNT, len);
+			for (i = 0; i < len; i++)
+				amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+
+		default:
+			printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size);
+			return -1;
+	}
+
+	amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
+	amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
+
+	amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+
+	if (~temp[0] & AMD_SMB_STS_DONE) {
+		udelay(500);
+		amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+	}
+
+	if (~temp[0] & AMD_SMB_STS_DONE) {
+		i2c_delay(HZ/100);
+		amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+	}
+
+	if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
+		return -1;
+
+	if (read_write == I2C_SMBUS_WRITE)
+		return 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+		case I2C_SMBUS_PROC_CALL:
+			amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
+			amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
+			data->word = (temp[1] << 8) | temp[0];
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+			len = min_t(u8, len, 32);
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			for (i = 0; i < len; i++)
+				amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1);
+			data->block[0] = len;
+			break;
+	}
+
+	return 0;
+}
+
+static void amd8111_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void amd8111_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u32 amd8111_func(struct i2c_adapter *adapter)
+{
+	return	I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+		I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+		I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name = "Non-I2C SMBus 2.0 adapter",
+	.id = I2C_ALGO_SMBUS,
+	.smbus_xfer = amd8111_access,
+	.functionality = amd8111_func,
+};
+
+
+static struct pci_device_id amd8111_ids[] __devinitdata = {
+	{ 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, }
+};
+
+static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct amd_smbus *smbus;
+	int error;
+
+	if (~pci_resource_flags(dev, 0) & IORESOURCE_IO)
+		return -1;
+
+	if (!(smbus = kmalloc(sizeof(struct amd_smbus), GFP_KERNEL)))
+		return -1;
+	memset(smbus, 0, sizeof(struct amd_smbus));
+
+	pci_set_drvdata(dev, smbus);
+	smbus->dev = dev;
+	smbus->base = pci_resource_start(dev, 0);
+	smbus->size = pci_resource_len(dev, 0);
+
+	if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) {
+		kfree(smbus);
+		return -1;
+	}
+
+	sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base);
+	smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111;
+	smbus->adapter.algo = &smbus_algorithm;
+	smbus->adapter.algo_data = smbus;
+	smbus->adapter.inc_use = amd8111_inc;
+	smbus->adapter.dec_use = amd8111_dec;
+
+	error = i2c_add_adapter(&smbus->adapter);
+	if (error) {
+		printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n");
+		release_region(smbus->base, smbus->size);
+		kfree(smbus);
+		return -1;
+	}
+
+	pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);
+
+	printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at %#x\n", smbus->base);
+	return 0;
+}
+
+
+static void __devexit amd8111_remove(struct pci_dev *dev)
+{
+	struct amd_smbus *smbus = (void*) pci_get_drvdata(dev);
+	i2c_del_adapter(&smbus->adapter);
+	release_region(smbus->base, smbus->size);
+	kfree(smbus);
+}
+
+static struct pci_driver amd8111_driver = {
+	.name		= "amd8111 smbus 2.0",
+	.id_table	= amd8111_ids,
+	.probe		= amd8111_probe,
+	.remove		= __devexit_p(amd8111_remove),
+};
+
+static int __init i2c_amd8111_init(void)
+{
+	printk(KERN_INFO "i2c-amd8111.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&amd8111_driver);
+}
+
+
+static void __exit i2c_amd8111_exit(void)
+{
+	pci_unregister_driver(&amd8111_driver);
+}
+
+module_init(i2c_amd8111_init);
+module_exit(i2c_amd8111_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-via.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-via.c	(revision 3148)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-via.c	(revision 3148)
@@ -0,0 +1,199 @@
+/*
+    i2c-via.c - Part of lm_sensors,  Linux kernel modules
+                for hardware monitoring
+
+    i2c Support for Via Technologies 82C586B South Bridge
+
+    Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#include "version.h"
+#include "sensors_compat.h"
+
+/* Power management registers */
+
+#define PM_CFG_REVID    0x08	/* silicon revision code */
+#define PM_CFG_IOBASE0  0x20
+#define PM_CFG_IOBASE1  0x48
+
+#define I2C_DIR		(pm_io_base+0x40)
+#define I2C_OUT		(pm_io_base+0x42)
+#define I2C_IN		(pm_io_base+0x44)
+#define I2C_SCL		0x02	/* clock bit in DIR/OUT/IN register */
+#define I2C_SDA		0x04
+
+/* io-region reservation */
+#define IOSPACE		0x06
+#define IOTEXT		"via-i2c"
+
+static u16 pm_io_base = 0;
+
+/*
+   It does not appear from the datasheet that the GPIO pins are
+   open drain. So a we set a low value by setting the direction to
+   output and a high value by setting the direction to input and
+   relying on the required I2C pullup. The data value is initialized
+   to 0 in via_init() and never changed.
+*/
+
+static void bit_via_setscl(void *data, int state)
+{
+	outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL,
+	     I2C_DIR);
+}
+
+static void bit_via_setsda(void *data, int state)
+{
+	outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA,
+	     I2C_DIR);
+}
+
+static int bit_via_getscl(void *data)
+{
+	return (0 != (inb(I2C_IN) & I2C_SCL));
+}
+
+static int bit_via_getsda(void *data)
+{
+	return (0 != (inb(I2C_IN) & I2C_SDA));
+}
+
+static void bit_via_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void bit_via_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_bit_data bit_data = {
+	.setsda		= bit_via_setsda,
+	.setscl		= bit_via_setscl,
+	.getsda		= bit_via_getsda,
+	.getscl		= bit_via_getscl,
+	.udelay		= 5,
+	.mdelay		= 5,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter vt586b_adapter = {
+	.name		= "VIA i2c",
+	.id		= I2C_HW_B_VIA,
+	.algo_data	= &bit_data,
+	.inc_use	= bit_via_inc,
+	.dec_use	= bit_via_dec,
+};
+
+
+static struct pci_device_id vt586b_ids[] __initdata = {
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, }
+};
+
+static int __init vt586b_probe(struct pci_dev *dev,
+			       const struct pci_device_id *id)
+{
+	u16 base;
+	u8 rev;
+	int res;
+
+	if (pm_io_base) {
+		printk(KERN_ERR "i2c-via.o: Will only support one host\n");
+		return -EBUSY;
+	}
+
+	pci_read_config_byte(dev, PM_CFG_REVID, &rev);
+
+	switch (rev) {
+	case 0x00:
+		base = PM_CFG_IOBASE0;
+		break;
+	case 0x01:
+	case 0x10:
+		base = PM_CFG_IOBASE1;
+		break;
+
+	default:
+		base = PM_CFG_IOBASE1;
+		/* later revision */
+	}
+
+	pci_read_config_word(dev, base, &pm_io_base);
+	pm_io_base &= (0xff << 8);
+
+	if (! request_region(I2C_DIR, IOSPACE, IOTEXT)) {
+	    printk("i2c-via.o: IO 0x%x-0x%x already in use\n",
+		   I2C_DIR, I2C_DIR + IOSPACE);
+	    return -EBUSY;
+	}
+	outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
+	outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
+	
+	res = i2c_bit_add_bus(&vt586b_adapter);
+	if ( res < 0 ) {
+		release_region(I2C_DIR, IOSPACE);
+		pm_io_base = 0;
+		return res;
+	}
+	return 0;
+}
+
+static int __init i2c_vt586b_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	pci_for_each_dev(dev) {
+		id = pci_match_device(vt586b_ids, dev);
+		if(id)
+			if(vt586b_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_vt586b_exit(void)
+{
+	i2c_bit_del_bus(&vt586b_adapter);
+	release_region(I2C_DIR, IOSPACE);
+}
+
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_vt586b_init);
+module_exit(i2c_vt586b_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-tsunami.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-tsunami.c	(revision 2802)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-tsunami.c	(revision 2802)
@@ -0,0 +1,170 @@
+/*
+    i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 2001  Oleg Vdovikin <vdovikin@jscc.ru>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    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 interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets 
+   to gain access to the on-board I2C devices. 
+
+   For more information refer to Compaq's 
+	"Tsunami/Typhoon 21272 Chipset Hardware Reference Manual"
+	Order Number: DS-0025-TE
+*/ 
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/core_tsunami.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+
+/* Memory Presence Detect Register (MPD-RW) bits 
+   with except of reserved RAZ bits */
+
+#define MPD_DR	0x8	/* Data receive - RO */
+#define MPD_CKR	0x4	/* Clock receive - RO */
+#define MPD_DS	0x2	/* Data send - Must be a 1 to receive - WO */
+#define MPD_CKS	0x1	/* Clock send - WO */
+
+static inline void writempd(unsigned long value)
+{
+	TSUNAMI_cchip->mpd.csr = value;
+	mb();
+}
+
+static inline unsigned long readmpd(void)
+{
+	return TSUNAMI_cchip->mpd.csr;
+}
+
+static void bit_tsunami_setscl(void *data, int val)
+{
+	/* read currently setted bits to modify them */
+	unsigned long bits = readmpd() >> 2; /* assume output == input */
+
+	if (val)
+		bits |= MPD_CKS;
+	else
+		bits &= ~MPD_CKS;
+
+	writempd(bits);
+}
+
+static void bit_tsunami_setsda(void *data, int val)
+{
+	/* read currently setted bits to modify them */
+	unsigned long bits = readmpd() >> 2; /* assume output == input */
+
+	if (val)
+		bits |= MPD_DS;
+	else
+		bits &= ~MPD_DS;
+
+	writempd(bits);
+}
+
+/* The MPD pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_tsunami_getscl(void *data)
+{
+	return (0 != (readmpd() & MPD_CKR));
+}
+
+static int bit_tsunami_getsda(void *data)
+{
+	return (0 != (readmpd() & MPD_DR));
+}
+
+static void i2c_tsunami_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void i2c_tsunami_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_bit_data tsunami_i2c_bit_data = {
+	.setsda		= bit_tsunami_setsda,
+	.setscl		= bit_tsunami_setscl,
+	.getsda		= bit_tsunami_getsda,
+	.getscl		= bit_tsunami_getscl,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= HZ/2
+};
+
+static struct i2c_adapter tsunami_i2c_adapter = {
+	.name		= "I2C Tsunami/Typhoon adapter",
+	.id		= I2C_HW_B_TSUNA,
+	.algo_data	= &tsunami_i2c_bit_data,
+	.inc_use	= i2c_tsunami_inc,
+	.dec_use	= i2c_tsunami_dec,
+};
+
+
+#if 0
+static struct pci_driver tsunami_driver = {
+	.name		= "tsunami smbus",
+	.id_table	= tsunami_ids,
+	.probe		= tsunami_probe,
+	.remove		= __devexit_p(tsunami_remove),
+};
+#endif
+
+static int __init i2c_tsunami_init(void)
+{
+	printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (hwrpb->sys_type != ST_DEC_TSUNAMI) {
+		printk("i2c-tsunami.o: not Tsunami based system (%ld), module not inserted.\n", hwrpb->sys_type);
+		return -ENXIO;
+	} else {
+		printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", (long) &TSUNAMI_cchip->mpd);
+	}
+	return i2c_bit_add_bus(&tsunami_i2c_adapter);
+}
+
+
+static void __exit i2c_tsunami_exit(void)
+{
+	i2c_bit_del_bus(&tsunami_i2c_adapter);
+}
+
+
+
+MODULE_AUTHOR("Oleg I. Vdovikin <vdovikin@jscc.ru>");
+MODULE_DESCRIPTION("Tsunami I2C/SMBus driver");
+
+module_init(i2c_tsunami_init);
+module_exit(i2c_tsunami_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i810.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i810.c	(revision 3160)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i810.c	(revision 3160)
@@ -0,0 +1,327 @@
+/*
+    i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    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 interfaces to the I810/I815 to provide access to
+   the DDC Bus and the I2C Bus.
+
+   SUPPORTED DEVICES	PCI ID
+   i810AA		7121           
+   i810AB		7123           
+   i810E		7125           
+   i815			1132           
+   i845G		2562
+*/
+
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#include "version.h"
+#include "sensors_compat.h"
+
+MODULE_LICENSE("GPL");
+
+/* Not defined by any Linux 2.4 kernel */
+#define PCI_DEVICE_ID_INTEL_82810E_IG	0x7125
+#define PCI_DEVICE_ID_INTEL_82815_CGC	0x1132
+#define PCI_DEVICE_ID_INTEL_82845G_IG	0x2562
+
+/* GPIO register locations */
+#define I810_IOCONTROL_OFFSET 0x5000
+#define I810_HVSYNC	0x00	/* not used */
+#define I810_GPIOA	0x10
+#define I810_GPIOB	0x14
+
+/* bit locations in the registers */
+#define SCL_DIR_MASK	0x0001
+#define SCL_DIR		0x0002
+#define SCL_VAL_MASK	0x0004
+#define SCL_VAL_OUT	0x0008
+#define SCL_VAL_IN	0x0010
+#define SDA_DIR_MASK	0x0100
+#define SDA_DIR		0x0200
+#define SDA_VAL_MASK	0x0400
+#define SDA_VAL_OUT	0x0800
+#define SDA_VAL_IN	0x1000
+
+/* initialization states */
+#define INIT1	0x1
+#define INIT2	0x2
+#define INIT3	0x4
+
+/* delays */
+#define CYCLE_DELAY		10
+#define TIMEOUT			(HZ / 2)
+
+
+static void config_i810(struct pci_dev *dev);
+
+
+static unsigned long ioaddr;
+
+/* The i810 GPIO registers have individual masks for each bit
+   so we never have to read before writing. Nice. */
+
+static void bit_i810i2c_setscl(void *data, int val)
+{
+	writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     ioaddr + I810_GPIOB);
+	readl(ioaddr + I810_GPIOB);	/* flush posted write */
+}
+
+static void bit_i810i2c_setsda(void *data, int val)
+{
+ 	writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     ioaddr + I810_GPIOB);
+	readl(ioaddr + I810_GPIOB);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins could always remain outputs.
+   However, some chip versions don't latch the inputs unless they
+   are set as inputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. Following guidance in the 815
+   prog. ref. guide, we do a "dummy write" of 0 to the register before
+   reading which forces the input value to be latched. We presume this
+   applies to the 810 as well; shouldn't hurt anyway. This is necessary to get
+   i2c_algo_bit bit_test=1 to pass. */
+
+static int bit_i810i2c_getscl(void *data)
+{
+	writel(SCL_DIR_MASK, ioaddr + I810_GPIOB);
+	writel(0, ioaddr + I810_GPIOB);
+	return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN));
+}
+
+static int bit_i810i2c_getsda(void *data)
+{
+	writel(SDA_DIR_MASK, ioaddr + I810_GPIOB);
+	writel(0, ioaddr + I810_GPIOB);
+	return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN));
+}
+
+static void bit_i810ddc_setscl(void *data, int val)
+{
+	writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     ioaddr + I810_GPIOA);
+	readl(ioaddr + I810_GPIOA);	/* flush posted write */
+}
+
+static void bit_i810ddc_setsda(void *data, int val)
+{
+ 	writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     ioaddr + I810_GPIOA);
+	readl(ioaddr + I810_GPIOA);	/* flush posted write */
+}
+
+static int bit_i810ddc_getscl(void *data)
+{
+	writel(SCL_DIR_MASK, ioaddr + I810_GPIOA);
+	writel(0, ioaddr + I810_GPIOA);
+	return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN));
+}
+
+static int bit_i810ddc_getsda(void *data)
+{
+	writel(SDA_DIR_MASK, ioaddr + I810_GPIOA);
+	writel(0, ioaddr + I810_GPIOA);
+	return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN));
+}
+
+
+/* Configures the chip */
+void config_i810(struct pci_dev *dev)
+{
+	unsigned long cadr;
+
+	/* map I810 memory */
+	cadr = dev->resource[1].start;
+	cadr += I810_IOCONTROL_OFFSET;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
+	if(ioaddr) {
+		bit_i810i2c_setscl(NULL, 1);
+		bit_i810i2c_setsda(NULL, 1);
+		bit_i810ddc_setscl(NULL, 1);
+		bit_i810ddc_setsda(NULL, 1);
+	}
+}
+
+static void i810_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void i810_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_bit_data i810_i2c_bit_data = {
+	.setsda		= bit_i810i2c_setsda,
+	.setscl		= bit_i810i2c_setscl,
+	.getsda		= bit_i810i2c_getsda,
+	.getscl		= bit_i810i2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT,
+};
+
+static struct i2c_adapter i810_i2c_adapter = {
+	.name		= "I810/I815 I2C Adapter",
+	.id		= I2C_HW_B_I810,
+	.algo_data	= &i810_i2c_bit_data,
+	.inc_use	= i810_inc,
+	.dec_use	= i810_dec,
+};
+
+static struct i2c_algo_bit_data i810_ddc_bit_data = {
+	.setsda		= bit_i810ddc_setsda,
+	.setscl		= bit_i810ddc_setscl,
+	.getsda		= bit_i810ddc_getsda,
+	.getscl		= bit_i810ddc_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT,
+};
+
+static struct i2c_adapter i810_ddc_adapter = {
+	.name		= "I810/I815 DDC Adapter",
+	.id		= I2C_HW_B_I810,
+	.algo_data	= &i810_ddc_bit_data,
+	.inc_use	= i810_inc,
+	.dec_use	= i810_dec,
+};
+
+
+static struct pci_device_id i810_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82810_IG1,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82810_IG3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82810E_IG,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82815_CGC,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82845G_IG,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int retval;
+
+	config_i810(dev);
+	printk("i2c-i810.o: i810/i815 found.\n");
+
+	retval = i2c_bit_add_bus(&i810_i2c_adapter);
+	if(retval)
+		return retval;
+	retval = i2c_bit_add_bus(&i810_ddc_adapter);
+	if(retval)
+		i2c_bit_del_bus(&i810_i2c_adapter);
+	return retval;
+}
+
+static void __devexit i810_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&i810_ddc_adapter);
+	i2c_bit_del_bus(&i810_i2c_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver i810_driver = {
+	.name		= "i810 smbus",
+	.id_table	= i810_ids,
+	.probe		= i810_probe,
+	.remove		= __devexit_p(i810_remove),
+};
+*/
+
+static int __init i2c_i810_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&i810_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(i810_ids, dev);
+		if(id)
+			if(i810_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit i2c_i810_exit(void)
+{
+/*
+	pci_unregister_driver(&i810_driver);
+*/
+	i810_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("I810/I815 I2C/DDC driver");
+
+module_init(i2c_i810_init);
+module_exit(i2c_i810_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i801.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i801.c	(revision 4061)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/busses/i2c-i801.c	(revision 4061)
@@ -0,0 +1,724 @@
+/*
+    i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>, 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.
+*/
+
+/*
+    SUPPORTED DEVICES	PCI ID
+    82801AA		2413           
+    82801AB		2423           
+    82801BA		2443           
+    82801CA/CAM		2483           
+    82801DB		24C3   (HW PEC supported, 32 byte buffer not supported)
+    82801EB		24D3   (HW PEC supported, 32 byte buffer not supported)
+    6300ESB		25A4   ("")
+    ICH6		266A   ("")
+    ICH7		27DA   ("")
+    ESB2 		269B   ("")
+    ICH8		283E   ("")
+
+    This driver supports several versions of Intel's I/O Controller Hubs (ICH).
+    For SMBus support, they are similar to the PIIX4 and are part
+    of Intel's '810' and other chipsets.
+    See the doc/busses/i2c-i801 file for details.
+    I2C Block Read supported for ICH5 and higher.
+    Block Process Call are not supported.
+*/
+
+/* Note: we assume there can only be one I801, with one SMBus interface */
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_compat.h"
+
+/* 82801CA is undefined before kernel 2.4.13 */
+#ifndef PCI_DEVICE_ID_INTEL_82801CA_3
+#define PCI_DEVICE_ID_INTEL_82801CA_3	0x2483
+#endif
+
+/* 82801DB is undefined before kernel 2.4.19 */
+#ifndef PCI_DEVICE_ID_INTEL_82801DB_3
+#define PCI_DEVICE_ID_INTEL_82801DB_3	0x24c3
+#endif
+
+/* 82801EB is undefined before kernel 2.4.21 */
+#ifndef PCI_DEVICE_ID_INTEL_82801EB_3
+#define PCI_DEVICE_ID_INTEL_82801EB_3	0x24d3
+#endif
+
+/* ESB is undefined before kernel 2.4.22 */
+#ifndef PCI_DEVICE_ID_INTEL_ESB_4
+#define PCI_DEVICE_ID_INTEL_ESB_4	0x25a4
+#endif
+
+/* ESB2 - Enterprise Southbridge is undefined */
+#ifndef PCI_DEVICE_ID_INTEL_ESB2_17
+#define PCI_DEVICE_ID_INTEL_ESB2_17	0x269b
+#endif
+
+/* ICH6 is undefined */
+#ifndef PCI_DEVICE_ID_INTEL_ICH6_16
+#define PCI_DEVICE_ID_INTEL_ICH6_16	0x266a
+#endif
+
+/* ICH7 is undefined */
+#ifndef PCI_DEVICE_ID_INTEL_ICH7_17
+#define PCI_DEVICE_ID_INTEL_ICH7_17	0x27da
+#endif
+
+/* ICH8 is undefined */
+#ifndef PCI_DEVICE_ID_INTEL_ICH8_5
+#define PCI_DEVICE_ID_INTEL_ICH8_5	0x283e
+#endif
+
+#ifdef I2C_CLIENT_PEC
+#define HAVE_PEC
+#endif
+
+/* I801 SMBus address offsets */
+#define SMBHSTSTS	(0 + i801_smba)
+#define SMBHSTCNT	(2 + i801_smba)
+#define SMBHSTCMD	(3 + i801_smba)
+#define SMBHSTADD	(4 + i801_smba)
+#define SMBHSTDAT0	(5 + i801_smba)
+#define SMBHSTDAT1	(6 + i801_smba)
+#define SMBBLKDAT	(7 + i801_smba)
+#define SMBPEC		(8 + i801_smba)	/* ICH4 only */
+#define SMBAUXSTS	(12 + i801_smba)	/* ICH4 only */
+#define SMBAUXCTL	(13 + i801_smba)	/* ICH4 only */
+
+/* PCI Address Constants */
+#define SMBBA		0x020
+#define SMBHSTCFG	0x040
+#define SMBREV		0x008
+
+/* Host configuration bits for SMBHSTCFG */
+#define SMBHSTCFG_HST_EN	1
+#define SMBHSTCFG_SMB_SMI_EN	2
+#define SMBHSTCFG_I2C_EN	4
+
+/* Other settings */
+#define MAX_TIMEOUT		100
+#define ENABLE_INT9		0	/* set to 0x01 to enable - untested */
+
+/* I801 command constants */
+#define I801_QUICK		0x00
+#define I801_BYTE		0x04
+#define I801_BYTE_DATA		0x08
+#define I801_WORD_DATA		0x0C
+#define I801_PROC_CALL		0x10	/* later chips only, unimplemented */
+#define I801_BLOCK_DATA		0x14
+#define I801_I2C_BLOCK_DATA	0x18	/* ich4 and later */
+#define I801_BLOCK_LAST		0x34
+#define I801_I2C_BLOCK_LAST	0x38	/* unimplemented */
+#define I801_START		0x40
+#define I801_PEC_EN		0x80	/* ich4 and later */
+
+/* insmod parameters */
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the I801 at the given address. VERY DANGEROUS! */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the I801 at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+static int i801_transaction(void);
+static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
+				  int command, int hwpec);
+
+static unsigned short i801_smba;
+static struct pci_driver i801_driver;
+static struct pci_dev *I801_dev;
+static int isich4;	/* is PEC supported? */
+static int isich5;	/* is i2c block read supported? */
+
+static int __devinit i801_setup(struct pci_dev *dev)
+{
+	unsigned char temp;
+
+	I801_dev = dev;
+	if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_3 ||
+	    dev->device == PCI_DEVICE_ID_INTEL_82801EB_3 ||
+	    dev->device == PCI_DEVICE_ID_INTEL_ESB_4 ||
+	    dev->device == PCI_DEVICE_ID_INTEL_ESB2_17 ||	
+	    dev->device == PCI_DEVICE_ID_INTEL_ICH6_16 ||
+	    dev->device == PCI_DEVICE_ID_INTEL_ICH7_17 ||
+	    dev->device == PCI_DEVICE_ID_INTEL_ICH8_5)
+		isich4 = 1;
+	else
+		isich4 = 0;
+	isich5 = isich4 && dev->device != PCI_DEVICE_ID_INTEL_82801DB_3;
+
+	/* Determine the address of the SMBus area */
+	if (force_addr) {
+		i801_smba = force_addr & 0xfff0;
+	} else {
+		pci_read_config_word(I801_dev, SMBBA, &i801_smba);
+		i801_smba &= 0xfff0;
+		if(i801_smba == 0) {
+			dev_err(dev, "SMB base address uninitialized "
+				"- upgrade BIOS or use force_addr=0xaddr\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!request_region(i801_smba, (isich4 ? 16 : 8), i801_driver.name)) {
+		dev_err(dev, "I801_smb region 0x%x already in use!\n",
+			i801_smba);
+		return -EBUSY;
+	}
+
+	pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
+	temp &= ~SMBHSTCFG_I2C_EN;	/* SMBus timing */
+	pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
+
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the device first. */
+	if (force_addr) {
+		pci_write_config_byte(I801_dev, SMBHSTCFG,
+				      temp & ~SMBHSTCFG_HST_EN);
+		pci_write_config_word(I801_dev, SMBBA, i801_smba);
+		pci_write_config_byte(I801_dev, SMBHSTCFG,
+				      temp | SMBHSTCFG_HST_EN);
+		dev_warn(dev, "WARNING: I801 SMBus interface set to "
+			"new address %04x!\n", i801_smba);
+	} else if (!(temp & SMBHSTCFG_HST_EN)) {
+		pci_write_config_byte(I801_dev, SMBHSTCFG,
+				      temp | SMBHSTCFG_HST_EN);
+		dev_warn(dev, "enabling SMBus device\n");
+	}
+
+	if (temp & SMBHSTCFG_SMB_SMI_EN)
+		dev_dbg(dev, "I801 using Interrupt SMI# for SMBus.\n");
+	else
+		dev_dbg(dev, "I801 using PCI Interrupt for SMBus.\n");
+
+	pci_read_config_byte(I801_dev, SMBREV, &temp);
+	dev_dbg(dev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(dev, "I801_smba = 0x%X\n", i801_smba);
+
+	return 0;
+}
+
+
+static int i801_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(I801_dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		inb_p(SMBHSTDAT1));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+		dev_dbg(I801_dev, "SMBus busy (%02x). Resetting...\n",
+			temp);
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+			dev_dbg(I801_dev, "Failed! (%02x)\n", temp);
+			return -1;
+		} else {
+			dev_dbg(I801_dev, "Successfull!\n");
+		}
+	}
+
+	outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		dev_dbg(I801_dev, "SMBus Timeout!\n");
+		result = -1;
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+		dev_dbg(I801_dev, "Error: Failed bus transaction\n");
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		dev_err(I801_dev, "Bus collision! SMBus may be locked "
+			"until next hard reset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+		dev_dbg(I801_dev, "Error: no response!\n");
+	}
+
+	if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+		dev_dbg(I801_dev, "Failed reset at end of transaction "
+			"(%02x)\n", temp);
+	}
+	dev_dbg(I801_dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		inb_p(SMBHSTDAT1));
+	return result;
+}
+
+/* All-inclusive block transaction function */
+static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
+				  int command, int hwpec)
+{
+	int i, len;
+	int smbcmd;
+	int temp;
+	int result = 0;
+	int timeout;
+	unsigned char hostc, errmask;
+
+	if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* set I2C_EN bit in configuration register */
+			pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
+			pci_write_config_byte(I801_dev, SMBHSTCFG,
+					      hostc | SMBHSTCFG_I2C_EN);
+		} else if (!isich5) {
+			dev_err(I801_dev,
+				"I2C_SMBUS_I2C_BLOCK_READ unsupported!\n");
+			return -1;
+		}
+	}
+
+	if (read_write == I2C_SMBUS_WRITE) {
+		len = data->block[0];
+		if (len < 1)
+			len = 1;
+		if (len > 32)
+			len = 32;
+		outb_p(len, SMBHSTDAT0);
+		outb_p(data->block[1], SMBBLKDAT);
+	} else {
+		len = 32;	/* max for reads */
+	}
+
+	if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+		/* set 32 byte buffer */
+	}
+
+	for (i = 1; i <= len; i++) {
+		if (i == len && read_write == I2C_SMBUS_READ)
+			smbcmd = I801_BLOCK_LAST;
+		else if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
+		         read_write == I2C_SMBUS_READ)
+			smbcmd = I801_I2C_BLOCK_DATA;
+		else
+			smbcmd = I801_BLOCK_DATA;
+		outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
+
+		dev_dbg(I801_dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
+			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+		/* Make sure the SMBus host is ready to start transmitting */
+		temp = inb_p(SMBHSTSTS);
+		if (i == 1) {
+			/* Erronenous conditions before transaction: 
+			 * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+			errmask=0x9f; 
+		} else {
+			/* Erronenous conditions during transaction: 
+			 * Failed, Bus_Err, Dev_Err, Intr */
+			errmask=0x1e; 
+		}
+		if (temp & errmask) {
+			dev_dbg(I801_dev, "SMBus busy (%02x). "
+				"Resetting...\n", temp);
+			outb_p(temp, SMBHSTSTS);
+			if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+				dev_err(I801_dev,
+					"Reset failed! (%02x)\n", temp);
+				result = -1;
+                                goto END;
+			}
+			if (i != 1) {
+				/* if die in middle of block transaction, fail */
+				result = -1;
+				goto END;
+			}
+		}
+
+		if (i == 1)
+			outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+
+		/* We will always wait for a fraction of a second! */
+		timeout = 0;
+		do {
+			i2c_delay(1);
+			temp = inb_p(SMBHSTSTS);
+		}
+		    while ((!(temp & 0x80))
+			   && (timeout++ < MAX_TIMEOUT));
+
+		/* If the SMBus is still busy, we give up */
+		if (timeout >= MAX_TIMEOUT) {
+			result = -1;
+			dev_dbg(I801_dev, "SMBus Timeout!\n");
+		}
+
+		if (temp & 0x10) {
+			result = -1;
+			dev_dbg(I801_dev,
+				"Error: Failed bus transaction\n");
+		} else if (temp & 0x08) {
+			result = -1;
+			dev_err(I801_dev, "Bus collision!\n");
+		} else if (temp & 0x04) {
+			result = -1;
+			dev_dbg(I801_dev, "Error: no response!\n");
+		}
+
+		if (i == 1 && read_write == I2C_SMBUS_READ) {
+			if (command != I2C_SMBUS_I2C_BLOCK_DATA) {
+				len = inb_p(SMBHSTDAT0);
+				if (len < 1)
+					len = 1;
+				if (len > 32)
+					len = 32;
+				data->block[0] = len;
+			} else {
+				/* if slave returns < 32 bytes transaction will fail */
+				data->block[0] = 32;
+			}
+		}
+
+		/* Retrieve/store value in SMBBLKDAT */
+		if (read_write == I2C_SMBUS_READ)
+			data->block[i] = inb_p(SMBBLKDAT);
+		if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
+			outb_p(data->block[i+1], SMBBLKDAT);
+		if ((temp & 0x9e) != 0x00)
+			outb_p(temp, SMBHSTSTS);  /* signals SMBBLKDAT ready */
+
+		if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+			dev_dbg(I801_dev,
+				"Bad status (%02x) at end of transaction\n",
+				temp);
+		}
+		dev_dbg(I801_dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
+			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+		if (result < 0)
+			goto END;
+	}
+
+	if (hwpec) {
+		/* wait for INTR bit as advised by Intel */
+		timeout = 0;
+		do {
+			i2c_delay(1);
+			temp = inb_p(SMBHSTSTS);
+		} while ((!(temp & 0x02))
+			   && (timeout++ < MAX_TIMEOUT));
+
+		if (timeout >= MAX_TIMEOUT) {
+			dev_dbg(I801_dev, "PEC Timeout!\n");
+		}
+		outb_p(temp, SMBHSTSTS); 
+	}
+	result = 0;
+END:
+	if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
+	    read_write == I2C_SMBUS_WRITE) {
+		/* restore saved configuration register value */
+		pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
+	}
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 i801_access(struct i2c_adapter * adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data * data)
+{
+	int hwpec = 0;
+	int block = 0;
+	int ret, xact = 0;
+
+#ifdef HAVE_PEC
+	hwpec = isich4 && (flags & I2C_CLIENT_PEC)
+		&& size != I2C_SMBUS_QUICK
+		&& size != I2C_SMBUS_I2C_BLOCK_DATA;
+#endif
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		xact = I801_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		xact = I801_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		xact = I801_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		xact = I801_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		block = 1;
+		break;
+	case I2C_SMBUS_PROC_CALL:
+	default:
+		dev_err(I801_dev, "Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p(hwpec, SMBAUXCTL);	/* enable/disable hardware PEC */
+
+	if(block)
+		ret = i801_block_transaction(data, read_write, size, hwpec);
+	else {
+		outb_p(xact | ENABLE_INT9, SMBHSTCNT);
+		ret = i801_transaction();
+	}
+
+	/* Some BIOSes don't like it when PEC is enabled at reboot or resume
+	   time, so we forcibly disable it after every transaction. */
+	if (hwpec)
+		outb_p(0, SMBAUXCTL);
+
+	if(block)
+		return ret;
+	if(ret)
+		return -1;
+	if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
+		return 0;
+
+	switch (xact & 0x7f) {
+	case I801_BYTE:	/* Result put in SMBHSTDAT0 */
+	case I801_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case I801_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	}
+	return 0;
+}
+
+static void i801_inc(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void i801_dec(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static u32 i801_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
+#ifdef HAVE_PEC
+	     | (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0)
+#endif
+#if 0
+	     | (isich5 ? I2C_FUNC_SMBUS_READ_I2C_BLOCK
+	               : 0)
+#endif
+	    ;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= i801_access,
+	.functionality	= i801_func,
+};
+
+static struct i2c_adapter i801_adapter = {
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801,
+	.algo		= &smbus_algorithm,
+	.inc_use	= i801_inc,
+	.dec_use	= i801_dec,
+};
+
+static struct pci_device_id i801_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801AA_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801AB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801BA_2,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801CA_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801DB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801EB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_ESB_4,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice = 	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_ESB2_17,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice = 	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_ICH6_16,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+  	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_ICH7_17,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_ICH8_5,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int err;
+
+	if ((err = i801_setup(dev)))
+		return err;
+
+	snprintf(i801_adapter.name, 32,
+		"SMBus I801 adapter at %04x", i801_smba);
+	return i2c_add_adapter(&i801_adapter);
+}
+
+static void __devexit i801_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&i801_adapter);
+	release_region(i801_smba, (isich4 ? 16 : 8));
+}
+
+static struct pci_driver i801_driver = {
+	.name		= "i801 smbus",
+	.id_table	= i801_ids,
+	.probe		= i801_probe,
+	.remove		= __devexit_p(i801_remove),
+};
+
+static int __init i2c_i801_init(void)
+{
+	printk(KERN_INFO "i2c-i801 version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&i801_driver);
+}
+
+static void __exit i2c_i801_exit(void)
+{
+	pci_unregister_driver(&i801_driver);
+}
+
+MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
+		"Philip Edelbrock <phil@netroedge.com>, "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("I801 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_i801_init);
+module_exit(i2c_i801_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/maxilife.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/maxilife.c	(revision 3147)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/maxilife.c	(revision 3147)
@@ -0,0 +1,1380 @@
+/*
+    maxilife.c - Part of lm_sensors, Linux kernel modules for hardware
+                 monitoring
+    Copyright (c) 1999-2000 Fons Rademakers <Fons.Rademakers@cern.ch> 
+
+    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.
+*/
+
+/* The is the driver for the HP MaxiLife Health monitoring system
+   as used in the line of HP Kayak Workstation PC's.
+   
+   The driver supports the following MaxiLife firmware versions:
+   
+   0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath)
+   1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz)
+   2) HP KAYAK XA (Pentium II Slot 1, monoprocessor)
+   
+   Currently firmware auto detection is not implemented. To use the
+   driver load it with the correct option for you Kayak. For example:
+   
+   insmod maxilife.o maxi_version=0 | 1 | 2
+   
+   maxi_version=0 is the default
+   
+   This version of MaxiLife is called MaxiLife'98 and has been
+   succeeded by MaxiLife'99, see below.
+   
+   The new version of the driver also supports MaxiLife NBA (New BIOS
+   Architecture). This new MaxiLife controller provides a much cleaner
+   machine independent abstraction layer to the MaxiLife controller.
+   Instead of accessing directly registers (different for each revision)
+   one now accesses the sensors via unique mailbox tokens that do not
+   change between revisions. Also the quantities are already in physical
+   units (degrees, rpms, voltages, etc.) and don't need special conversion
+   formulas. This new MaxiLife is available on the new 2000 machines,
+   like the Kayak XU800 and XM600. This hardware is also autodetected.
+*/
+
+static const char *version_str = "2.00 29/2/2000 Fons Rademakers";
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+#undef AUTODETECT		/* try to autodetect MaxiLife version */
+/*#define AUTODETECT*/
+#define NOWRITE			/* don't allow writing to MaxiLife registers */
+
+#ifdef AUTODETECT
+#include <linux/vmalloc.h>
+#include <linux/ctype.h>
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x10, 0x14, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(maxilife);
+
+/* Macro definitions */
+#define LOW(MyWord) ((u8) (MyWord))
+#define HIGH(MyWord) ((u8) (((u16)(MyWord) >> 8) & 0xFF))
+
+/*----------------- MaxiLife'98 registers and conversion formulas ------------*/
+#define MAXI_REG_TEMP(nr)      (0x60 + (nr))
+
+#define MAXI_REG_FAN(nr)       (0x65 + (nr))
+#define MAXI_REG_FAN_MIN(nr)   ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab)
+#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3)
+#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9)
+
+#define MAXI_REG_PLL           0xb9
+#define MAXI_REG_PLL_MIN       0xba
+#define MAXI_REG_PLL_MAX       0xbb
+
+#define MAXI_REG_VID(nr)       ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \
+                                (nr)==2 ? 0xd4 : 0xc5)
+#define MAXI_REG_VID_MIN(nr)   MAXI_REG_VID(nr)+1
+#define MAXI_REG_VID_MAX(nr)   MAXI_REG_VID(nr)+2
+
+#define MAXI_REG_DIAG_RT1      0x2c
+#define MAXI_REG_DIAG_RT2      0x2d
+
+#define MAXI_REG_BIOS_CTRL     0x2a
+
+/* 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. */
+
+			       /* 0xfe: fan off, 0xff: stopped (alarm) */
+			       /* 19531 / val * 60 == 1171860 / val */
+#define FAN_FROM_REG(val)      ((val)==0xfe ? 0 : (val)==0xff ? -1 : \
+                                (val)==0x00 ? -1 : (1171860 / (val)))
+
+static inline u8 FAN_TO_REG(long rpm)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1171860 + rpm / 2) / (rpm), 1, 254);
+}
+
+#define TEMP_FROM_REG(val)     ((val) * 5)
+#define TEMP_TO_REG(val)       (SENSORS_LIMIT((val+2) / 5),0,0xff)
+#define PLL_FROM_REG(val)      (((val) * 1000) / 32)
+#define PLL_TO_REG(val)        (SENSORS_LIMIT((((val) * 32 + 500) / 1000),\
+                                              0,0xff))
+#define VID_FROM_REG(val)      ((val) ? (((val) * 27390) / 256) + 3208 : 0)
+#define VID_TO_REG(val)        (SENSORS_LIMIT((((val) - 3208) * 256) / 27390, \
+                                              0,255))
+#define ALARMS_FROM_REG(val)   (val)
+
+/*----------------- MaxiLife'99 mailbox and token definitions ----------------*/
+/* MaxiLife mailbox data register map */
+#define MAXI_REG_MBX_STATUS    0x5a
+#define MAXI_REG_MBX_CMD       0x5b
+#define MAXI_REG_MBX_TOKEN_H   0x5c
+#define MAXI_REG_MBX_TOKEN_L   0x5d
+#define MAXI_REG_MBX_DATA      0x60
+
+/* Mailbox status register definition */
+#define MAXI_STAT_IDLE         0xff
+#define MAXI_STAT_OK           0x00
+#define MAXI_STAT_BUSY         0x0b
+/* other values not used */
+
+/* Mailbox command register opcodes */
+#define MAXI_CMD_READ          0x02
+#define MAXI_CMD_WRITE         0x03
+/* other values not used */
+
+/* MaxiLife NBA Hardware monitoring tokens */
+
+/* Alarm tokens (0x1xxx) */
+#define MAXI_TOK_ALARM(nr)    (0x1000 + (nr))
+#define MAXI_TOK_ALARM_EVENT   0x1000
+#define MAXI_TOK_ALARM_FAN     0x1001
+#define MAXI_TOK_ALARM_TEMP    0x1002
+#define MAXI_TOK_ALARM_VID     0x1003	/* voltages */
+#define MAXI_TOK_ALARM_AVID    0x1004	/* additional voltages */
+#define MAXI_TOK_ALARM_PWR     0x1101	/* power supply glitch */
+
+/* Fan status tokens (0x20xx) */
+#define MAXI_TOK_FAN(nr)      (0x2000 + (nr))
+#define MAXI_TOK_FAN_CPU       0x2000
+#define MAXI_TOK_FAN_PCI       0x2001
+#define MAXI_TOK_FAN_HDD       0x2002	/* hard disk bay fan */
+#define MAXI_TOK_FAN_SINK      0x2003	/* heatsink */
+
+/* Temperature status tokens (0x21xx) */
+#define MAXI_TOK_TEMP(nr)     (0x2100 + (nr))
+#define MAXI_TOK_TEMP_CPU1     0x2100
+#define MAXI_TOK_TEMP_CPU2     0x2101
+#define MAXI_TOK_TEMP_PCI      0x2102	/* PCI/ambient temp */
+#define MAXI_TOK_TEMP_HDD      0x2103	/* hard disk bay temp */
+#define MAXI_TOK_TEMP_MEM      0x2104	/* mother board temp */
+#define MAXI_TOK_TEMP_CPU      0x2105	/* CPU reference temp */
+
+/* Voltage status tokens (0x22xx) */
+#define MAXI_TOK_VID(nr)      (0x2200 + (nr))
+#define MAXI_TOK_VID_12        0x2200	/* +12 volt */
+#define MAXI_TOK_VID_CPU1      0x2201	/* cpu 1 voltage */
+#define MAXI_TOK_VID_CPU2      0x2202	/* cpu 2 voltage */
+#define MAXI_TOK_VID_L2        0x2203	/* level 2 cache voltage */
+#define MAXI_TOK_VID_M12       0x2204	/* -12 volt */
+
+/* Additive voltage status tokens (0x23xx) */
+#define MAXI_TOK_AVID(nr)     (0x2300 + (nr))
+#define MAXI_TOK_AVID_15       0x2300	/* 1.5 volt */
+#define MAXI_TOK_AVID_18       0x2301	/* 1.8 volt */
+#define MAXI_TOK_AVID_25       0x2302	/* 2.5 volt */
+#define MAXI_TOK_AVID_33       0x2303	/* 3.3 volt */
+#define MAXI_TOK_AVID_5        0x2304	/* 5 volt */
+#define MAXI_TOK_AVID_M5       0x2305	/* -5 volt */
+#define MAXI_TOK_AVID_BAT      0x2306	/* battery voltage */
+
+/* Threshold tokens (0x3xxx) */
+#define MAXI_TOK_MIN(token)    ((token) + 0x1000)
+#define MAXI_TOK_MAX(token)    ((token) + 0x1800)
+
+/* LCD Panel (0x4xxx) */
+#define MAXI_TOK_LCD(nr)      (0x4000 + (nr))
+#define MAXI_TOK_LCD_LINE1     0x4000
+#define MAXI_TOK_LCD_LINE2     0x4001
+#define MAXI_TOK_LCD_LINE3     0x4002
+#define MAXI_TOK_LCD_LINE4     0x4003
+
+			       /* 0xfe: fan off, 0xff: stopped (alarm) */
+			       /* or not available */
+#define FAN99_FROM_REG(val)    ((val)==0xfe ? 0 : (val)==0xff ? -1 : ((val)*39))
+
+			       /* when no CPU2 temp is 127 (0x7f) */
+#define TEMP99_FROM_REG(val)   ((val)==0x7f ? -1 : (val)==0xff ? -1 : (val))
+
+#define VID99_FROM_REG(nr,val) ((val)==0xff ? 0 : \
+                                (nr)==1 ? ((val) * 608) : \
+                                (nr)==2 ? ((val) * 160) : \
+                                (nr)==3 ? ((val) * 160) : \
+                                (nr)==4 ? (val) /* no formula spcified */ : \
+                                (nr)==5 ? ((val) * 823 - 149140) : 0)
+
+
+/* The following product codenames apply:
+     Cristal/Geronimo: HP KAYAK XU/XAs
+                       (Dual Pentium II Slot 1, Deschutes/Klamath)
+     Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz)
+     Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor)
+     NBA:    New BIOS Architecture, Kayak XU800, XM600, ... */
+
+enum maxi_type { cristal, cognac, ashaki, nba };
+enum sensor_type { fan, temp, vid, pll, lcd, alarm };
+
+/* For each registered MaxiLife controller, we need to keep some data in
+   memory. That data is pointed to by maxi_list[NR]->data. The structure
+   itself is dynamically allocated, at the same time when a new MaxiLife
+   client is allocated. We assume MaxiLife will only be present on the
+   SMBus and not on the ISA bus. */
+struct maxi_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum maxi_type type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 fan[4];		/* Register value */
+	u8 fan_min[4];		/* Register value */
+	u8 fan_speed[4];	/* Register value */
+	u8 fan_div[4];		/* Static value */
+	u8 temp[6];		/* Register value */
+	u8 temp_max[6];		/* Static value */
+	u8 temp_hyst[6];	/* Static value */
+	u8 pll;			/* Register value */
+	u8 pll_min;		/* Register value */
+	u8 pll_max;		/* register value */
+	u8 vid[5];		/* Register value */
+	u8 vid_min[5];		/* Register value */
+	u8 vid_max[5];		/* Register value */
+	u8 lcd[4][17];		/* Four LCD lines */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+static int maxi_attach_adapter(struct i2c_adapter *adapter);
+static int maxi_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int maxi_detach_client(struct i2c_client *client);
+
+static int maxi_read_value(struct i2c_client *client, u8 register);
+static int maxi_read_token(struct i2c_client *client, u16 token);
+#ifndef NOWRITE
+static int maxi_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+#endif
+static int maxi_write_token_loop(struct i2c_client *client, u16 token,
+				 u8 len, u8 * values);
+
+static void maxi_update_client(struct i2c_client *client);
+static void maxi99_update_client(struct i2c_client *client,
+				 enum sensor_type sensor, int which);
+static void maxi_init_client(struct i2c_client *client);
+
+static void maxi_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi99_fan(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void maxi_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void maxi99_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void maxi_pll(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi_vid(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi99_vid(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void maxi_lcd(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+/* The driver. I choose to use type i2c_driver, as at is identical to
+   the smbus_driver. */
+static struct i2c_driver maxi_driver = {
+	.name		= "HP MaxiLife driver",
+	.id		= I2C_DRIVERID_MAXILIFE,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= maxi_attach_adapter,
+	.detach_client	= maxi_detach_client,
+};
+
+/* Default firmware version. Use module option "maxi_version"
+   to set desired version. Auto detect is not yet working */
+static int maxi_version = cristal;
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define MAXI_SYSCTL_FAN1   1101	/* Rotations/min */
+#define MAXI_SYSCTL_FAN2   1102	/* Rotations/min */
+#define MAXI_SYSCTL_FAN3   1103	/* Rotations/min */
+#define MAXI_SYSCTL_FAN4   1104	/* Rotations/min */
+#define MAXI_SYSCTL_TEMP1  1201	/* Degrees Celsius */
+#define MAXI_SYSCTL_TEMP2  1202	/* Degrees Celsius */
+#define MAXI_SYSCTL_TEMP3  1203	/* Degrees Celsius */
+#define MAXI_SYSCTL_TEMP4  1204	/* Degrees Celsius */
+#define MAXI_SYSCTL_TEMP5  1205	/* Degrees Celsius */
+#define MAXI_SYSCTL_TEMP6  1206	/* Degrees Celsius */
+#define MAXI_SYSCTL_PLL    1301	/* MHz */
+#define MAXI_SYSCTL_VID1   1401	/* Volts / 6.337, for nba just Volts */
+#define MAXI_SYSCTL_VID2   1402	/* Volts */
+#define MAXI_SYSCTL_VID3   1403	/* Volts */
+#define MAXI_SYSCTL_VID4   1404	/* Volts */
+#define MAXI_SYSCTL_VID5   1405	/* Volts */
+#define MAXI_SYSCTL_LCD1   1501	/* Line 1 of LCD */
+#define MAXI_SYSCTL_LCD2   1502	/* Line 2 of LCD */
+#define MAXI_SYSCTL_LCD3   1503	/* Line 3 of LCD */
+#define MAXI_SYSCTL_LCD4   1504	/* Line 4 of LCD */
+#define MAXI_SYSCTL_ALARMS 2001	/* Bitvector (see below) */
+
+#define MAXI_ALARM_VID4      0x0001
+#define MAXI_ALARM_TEMP2     0x0002
+#define MAXI_ALARM_VID1      0x0004
+#define MAXI_ALARM_VID2      0x0008
+#define MAXI_ALARM_VID3      0x0010
+#define MAXI_ALARM_PLL       0x0080
+#define MAXI_ALARM_TEMP4     0x0100
+#define MAXI_ALARM_TEMP5     0x0200
+#define MAXI_ALARM_FAN1      0x1000
+#define MAXI_ALARM_FAN2      0x2000
+#define MAXI_ALARM_FAN3      0x4000
+
+#define MAXI_ALARM_FAN       0x0100	/* To be used with  MaxiLife'99 */
+#define MAXI_ALARM_VID       0x0200	/* The MSB specifies which sensor */
+#define MAXI_ALARM_TEMP      0x0400	/* in the alarm group failed, i.e.: */
+#define MAXI_ALARM_VADD      0x0800	/* 0x0402 = TEMP2 failed = CPU2 temp */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected MaxiLife processor.
+   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 maxi_dir_table_template[] = {
+	{MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_pll},
+	{MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_alarms},
+	{0}
+};
+
+/* This function is called when:
+    - maxi_driver is inserted (when this module is loaded), for each
+      available adapter
+    - when a new adapter is inserted (and maxi_driver is still present) */
+static int maxi_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, maxi_detect);
+}
+
+/* This function is called by i2c_detect */
+int maxi_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct maxi_data *data;
+	enum maxi_type type = 0;
+	int i, j, err = 0;
+	const char *type_name = NULL, *client_name = NULL;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* 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 maxi_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct maxi_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	/* Fill the new client structure with data */
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &maxi_driver;
+	new_client->flags = 0;
+
+	/* Now we do the remaining detection. */
+	if (kind < 0) {
+		if (i2c_smbus_read_byte_data
+		    (new_client, MAXI_REG_MBX_STATUS) < 0)
+			goto ERROR2;
+	}
+
+	/* Determine the chip type - only one kind supported */
+	if (kind <= 0)
+		kind = maxilife;
+
+	if (kind == maxilife) {
+		/* Detect if the machine has a MaxiLife NBA controller.
+		   The right way to perform this check is to do a read/modify/write
+		   on register MbxStatus (5A):
+		   - Read 5A (value 0 for non-NBA firmware, FF (MbxIdle on NBA-firmware)
+		   - Write 55 on 5A, then read back 5A
+		   Non-NBA firmware: value is 55 (reg 5A is a standard writable reg)
+		   NBA firmaware: value is FF (write-protect on MbxStatus active) */
+		int stat;
+		i2c_smbus_write_byte_data(new_client, MAXI_REG_MBX_STATUS,
+					  0x55);
+		stat =
+		    i2c_smbus_read_byte_data(new_client,
+					     MAXI_REG_MBX_STATUS);
+
+		/*if (stat == MAXI_STAT_IDLE || stat == MAXI_STAT_OK) */
+		if (stat != 0x55)
+			maxi_version = nba;
+#ifdef AUTODETECT
+		else {
+			/* The right way to get the platform info is to read the firmware
+			   revision from serial EEPROM (addr=0x54), at offset 0x0045.
+			   This is a string as:
+			   "CG 00.04" -> Cristal [XU] / Geronimo [XAs]
+			   "CO 00.03" -> Cognac [XU]
+			   "AS 00.01" -> Ashaki [XA] */
+#if 0
+			int biosctl;
+			biosctl =
+			    i2c_smbus_read_byte_data(new_client,
+						     MAXI_REG_BIOS_CTRL);
+			i2c_smbus_write_byte_data(new_client,
+						  MAXI_REG_BIOS_CTRL,
+						  biosctl | 4);
+			err = eeprom_read_byte_data(adapter, 0x54, 0x45);
+			i2c_smbus_write_byte_data(new_client,
+						  MAXI_REG_BIOS_CTRL,
+						  biosctl);
+#endif
+			int i;
+			char *biosmem, *bm;
+			bm = biosmem = ioremap(0xe0000, 0x20000);
+			if (biosmem) {
+				printk("begin of bios search\n");
+				for (i = 0; i < 0x20000; i++) {
+					if (*bm == 'C') {
+						char *s = bm;
+						while (s && isprint(*s)) {
+							printk("%c", *s);
+							s++;
+						}
+						printk("\n");
+						if (!strncmp
+						    (bm, "CG 00.04", 8)) {
+							maxi_version =
+							    cristal;
+							printk
+							    ("maxilife: found MaxiLife Rev CG 00.04\n");
+							break;
+						}
+						if (!strncmp
+						    (bm, "CO 00.03", 8)) {
+							maxi_version =
+							    cognac;
+							printk
+							    ("maxilife: found MaxiLife Rev CO 00.03\n");
+							break;
+						}
+					}
+					if (*bm == 'A' && *(bm + 1) == 'S') {
+						char *s = bm;
+						while (s && isprint(*s)) {
+							printk("%c", *s);
+							s++;
+						}
+						printk("\n");
+						if (!strncmp
+						    (bm, "AS 00.01", 8)) {
+							maxi_version =
+							    ashaki;
+							printk
+							    ("maxilife: found MaxiLife Rev AS 00.01\n");
+							break;
+						}
+					}
+					bm++;
+				}
+				printk("end of bios search\n");
+			} else
+				printk("could not map bios memory\n");
+		}
+#endif
+
+		if (maxi_version == cristal) {
+			type = cristal;
+			type_name = "maxilife-cg";
+			client_name = "HP MaxiLife Rev CG 00.04";
+			printk
+			    ("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n");
+		} else if (maxi_version == cognac) {
+			type = cognac;
+			type_name = "maxilife-co";
+			client_name = "HP MaxiLife Rev CO 00.03";
+			printk
+			    ("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n");
+		} else if (maxi_version == ashaki) {
+			type = ashaki;
+			type_name = "maxilife-as";
+			client_name = "HP MaxiLife Rev AS 00.01";
+			printk
+			    ("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n");
+		} else if (maxi_version == nba) {
+			type = nba;
+			type_name = "maxilife-nba";
+			client_name = "HP MaxiLife NBA";
+			printk("maxilife: HP KAYAK XU800/XM600\n");
+		} else {
+#ifdef AUTODETECT
+			printk
+			    ("maxilife: Warning: probed non-maxilife chip?!? (%x)\n",
+			     err);
+#else
+			printk
+			    ("maxilife: Error: specified wrong maxi_version (%d)\n",
+			     maxi_version);
+#endif
+			goto ERROR2;
+		}
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	((struct maxi_data *) (new_client->data))->type = type;
+
+	for (i = 0; i < 4; i++)
+		for (j = 0; j < 17; j++)
+			    ((struct maxi_data *) (new_client->data))->
+			    lcd[i][j] = (u8) 0;
+
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell i2c-core that a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR2;
+
+	/* Register a new directory entry with module sensors */
+	if ((err = i2c_register_entry(new_client, type_name,
+					  maxi_dir_table_template,
+					  THIS_MODULE)) < 0)
+		goto ERROR4;
+	data->sysctl_id = err;
+
+	/* Initialize the MaxiLife chip */
+	maxi_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);
+      ERROR2:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+/* This function is called whenever a client should be removed:
+    - maxi_driver is removed (when this module is unloaded)
+    - when an adapter is removed which has a maxi client (and maxi_driver
+      is still present). */
+static int maxi_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct maxi_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("maxilife: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+	return 0;
+}
+
+/* Read byte from specified register (-1 in case of error, value otherwise). */
+static int maxi_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Read the byte value for a MaxiLife token (-1 in case of error, value otherwise */
+static int maxi_read_token(struct i2c_client *client, u16 token)
+{
+	u8 lowToken, highToken;
+	int error, value;
+
+	lowToken = LOW(token);
+	highToken = HIGH(token);
+
+	/* Set mailbox status register to idle state. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	/* Check for mailbox idle state. */
+	error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	if (error != MAXI_STAT_IDLE)
+		return -1;
+
+	/* Write the most significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H,
+				      highToken);
+	if (error < 0)
+		return error;
+
+	/* Write the least significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L,
+				      lowToken);
+	if (error < 0)
+		return error;
+
+	/* Write the read token opcode to the mailbox. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD,
+				      MAXI_CMD_READ);
+	if (error < 0)
+		return error;
+
+	/* Check for transaction completion */
+	do {
+		error =
+		    i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	} while (error == MAXI_STAT_BUSY);
+	if (error != MAXI_STAT_OK)
+		return -1;
+
+	/* Read the value of the token. */
+	value = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_DATA);
+	if (value == -1)
+		return -1;
+
+	/* set mailbox status to idle to complete transaction. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	return value;
+}
+
+#ifndef NOWRITE
+/* Write byte to specified register (-1 in case of error, 0 otherwise). */
+static int maxi_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+#endif
+
+/* Write a set of len byte values to MaxiLife token (-1 in case of error, 0 otherwise). */
+int maxi_write_token_loop(struct i2c_client *client, u16 token, u8 len,
+			  u8 * values)
+{
+	u8 lowToken, highToken, bCounter;
+	int error;
+
+	lowToken = LOW(token);
+	highToken = HIGH(token);
+
+	/* Set mailbox status register to idle state. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	/* Check for mailbox idle state. */
+	error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	if (error != MAXI_STAT_IDLE)
+		return -1;
+
+	for (bCounter = 0; (bCounter < len && bCounter < 32); bCounter++) {
+		error =
+		    i2c_smbus_write_byte_data(client,
+					      (u8) (MAXI_REG_MBX_DATA +
+						    bCounter),
+					      values[bCounter]);
+		if (error < 0)
+			return error;
+	}
+
+	/* Write the most significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H,
+				      highToken);
+	if (error < 0)
+		return error;
+
+	/* Write the least significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L,
+				      lowToken);
+	if (error < 0)
+		return error;
+
+	/* Write the write token opcode to the mailbox. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD,
+				      MAXI_CMD_WRITE);
+	if (error < 0)
+		return error;
+
+	/* Check for transaction completion */
+	do {
+		error =
+		    i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	} while (error == MAXI_STAT_BUSY);
+	if (error != MAXI_STAT_OK)
+		return -1;
+
+	/* set mailbox status to idle to complete transaction. */
+	return i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+					 MAXI_STAT_IDLE);
+}
+
+/* Called when we have found a new MaxiLife. */
+static void maxi_init_client(struct i2c_client *client)
+{
+	struct maxi_data *data = client->data;
+
+	if (data->type == nba) {
+		strcpy(data->lcd[2], " Linux MaxiLife");
+		maxi_write_token_loop(client, MAXI_TOK_LCD(2),
+				      strlen(data->lcd[2]) + 1,
+				      data->lcd[2]);
+	}
+}
+
+static void maxi_update_client(struct i2c_client *client)
+{
+	struct maxi_data *data = client->data;
+	int i;
+
+	if (data->type == nba) {
+		printk
+		    ("maxi_update_client should never be called by nba\n");
+		return;
+	}
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("maxilife: Starting MaxiLife update\n");
+#endif
+		for (i = 0; i < 5; i++)
+			data->temp[i] =
+			    maxi_read_value(client, MAXI_REG_TEMP(i));
+		switch (data->type) {
+		case cristal:
+			data->temp[0] = 0;	/* not valid */
+			data->temp_max[0] = 0;
+			data->temp_hyst[0] = 0;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp_max[2] = 120;	/* max BX chipset temp */
+			data->temp_hyst[2] = 110;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		case cognac:
+			data->temp_max[0] = 120;	/* max CPU1 temp */
+			data->temp_hyst[0] = 110;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp_max[2] = 120;	/* max CPU2 temp */
+			data->temp_hyst[2] = 110;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max reference CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		case ashaki:
+			data->temp[0] = 0;	/* not valid */
+			data->temp_max[0] = 0;
+			data->temp_hyst[0] = 0;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp[2] = 0;	/* not valid */
+			data->temp_max[2] = 0;
+			data->temp_hyst[2] = 0;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		default:
+			printk("maxilife: Unknown MaxiLife chip\n");
+		}
+		data->temp[5] = 0;	/* only used by MaxiLife'99 */
+		data->temp_max[5] = 0;
+		data->temp_hyst[5] = 0;
+
+		for (i = 0; i < 3; i++) {
+			data->fan[i] =
+			    maxi_read_value(client, MAXI_REG_FAN(i));
+			data->fan_speed[i] =
+			    maxi_read_value(client, MAXI_REG_FAN_SPEED(i));
+			data->fan_div[i] = 4;
+			if (data->type == ashaki)
+				data->fan_min[i] =
+				    maxi_read_value(client,
+						    MAXI_REG_FAN_MINAS(i));
+			else
+				data->fan_min[i] =
+				    maxi_read_value(client,
+						    MAXI_REG_FAN_MIN(i));
+		}
+		data->fan[3] = 0xff;	/* only used by MaxiLife'99 */
+		data->fan_speed[3] = 0;
+		data->fan_div[3] = 4;	/* avoid possible /0 */
+		data->fan_min[3] = 0;
+
+		data->pll = maxi_read_value(client, MAXI_REG_PLL);
+		data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN);
+		data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX);
+
+		for (i = 0; i < 4; i++) {
+			data->vid[i] =
+			    maxi_read_value(client, MAXI_REG_VID(i));
+			data->vid_min[i] =
+			    maxi_read_value(client, MAXI_REG_VID_MIN(i));
+			data->vid_max[i] =
+			    maxi_read_value(client, MAXI_REG_VID_MAX(i));
+		}
+		switch (data->type) {
+		case cristal:
+			data->vid[3] = 0;	/* no voltage cache L2 */
+			data->vid_min[3] = 0;
+			data->vid_max[3] = 0;
+			break;
+
+		case cognac:
+			break;
+
+		case ashaki:
+			data->vid[1] = 0;	/* no voltage CPU 2 */
+			data->vid_min[1] = 0;
+			data->vid_max[1] = 0;
+			data->vid[3] = 0;	/* no voltage cache L2 */
+			data->vid_min[3] = 0;
+			data->vid_max[3] = 0;
+			break;
+
+		default:
+			printk("maxilife: Unknown MaxiLife chip\n");
+		}
+		data->vid[4] = 0;	/* only used by MaxliLife'99 */
+		data->vid_min[4] = 0;
+		data->vid_max[4] = 0;
+
+		data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) +
+		    (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void maxi99_update_client(struct i2c_client *client,
+			  enum sensor_type sensor, int which)
+{
+	static unsigned long last_updated[6][6];	/* sensor, which */
+	struct maxi_data *data = client->data;
+
+	down(&data->update_lock);
+
+	/*maxi_write_token_loop(client, MAXI_TOK_LCD_LINE3, 13, "Linux 2.2.13"); */
+
+	if ((jiffies - last_updated[sensor][which] > 2 * HZ) ||
+	    (jiffies < last_updated[sensor][which]
+	     || !last_updated[sensor][which])) {
+
+		int tmp, i;
+
+		switch (sensor) {
+		case fan:
+			for (i = 0; i < 4; i++) {
+				if (i == which) {
+					tmp =
+					    maxi_read_token(client,
+							    MAXI_TOK_FAN
+							    (i));
+					data->fan[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_FAN
+							    (i));
+					data->fan_speed[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_FAN
+							     (i)));
+					data->fan_div[i] = 1;
+					data->fan_min[i] = 0;
+				}
+			}
+			break;
+
+		case temp:
+			for (i = 0; i < 6; i++) {
+				if (i == which) {
+					data->temp[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_TEMP
+							    (i));
+					data->temp_max[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_TEMP
+							     (i)));
+					data->temp_hyst[i] =
+					    data->temp_max[i] - 5;
+				}
+			}
+			break;
+
+		case vid:
+			for (i = 0; i < 5; i++) {
+				if (i == which) {
+					data->vid[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_VID
+							    (i));
+					data->vid_min[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MIN
+							    (MAXI_TOK_VID
+							     (i)));
+					data->vid_max[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_VID
+							     (i)));
+				}
+			}
+			break;
+
+		case pll:
+			data->pll = 0;
+			data->pll_min = 0;
+			data->pll_max = 0;
+			break;
+
+		case alarm:
+			data->alarms =
+			    (maxi_read_token(client, MAXI_TOK_ALARM_EVENT)
+			     << 8);
+			if (data->alarms)
+				data->alarms +=
+				    data->alarms ==
+				    (1 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_FAN)
+				    : data->alarms ==
+				    (2 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_VID)
+				    : data->alarms ==
+				    (4 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_TEMP)
+				    : data->alarms ==
+				    (8 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_FAN)
+				    : 0;
+			break;
+
+		default:
+			printk("maxilife: Unknown sensor type\n");
+		}
+
+		last_updated[sensor][which] = 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 data
+   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. */
+void maxi_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_fan(client, operation, ctl_name, nrels_mag,
+			   results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1]);
+		results[1] = data->fan_div[nr - 1];
+		results[2] = FAN_FROM_REG(data->fan[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi99_fan(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	nr = ctl_name - MAXI_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, fan, nr - 1);
+		results[0] = FAN99_FROM_REG(data->fan_min[nr - 1]);	/* min rpm */
+		results[1] = data->fan_div[nr - 1];	/* divisor */
+		results[2] = FAN99_FROM_REG(data->fan[nr - 1]);	/* rpm */
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		/* still to do */
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_temp(client, operation, ctl_name, nrels_mag,
+			    results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_max[nr - 1]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr - 1]);
+		results[2] = TEMP_FROM_REG(data->temp[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* temperature range can not be changed */
+	}
+}
+
+void maxi99_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, temp, nr - 1);
+		results[0] = TEMP99_FROM_REG(data->temp_max[nr - 1]);
+		results[1] = TEMP99_FROM_REG(data->temp_hyst[nr - 1]);
+		results[2] = TEMP99_FROM_REG(data->temp[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* temperature range can not be changed */
+	}
+}
+
+void maxi_pll(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (data->type == nba)
+			maxi99_update_client(client, pll, 0);
+		else
+			maxi_update_client(client);
+		results[0] = PLL_FROM_REG(data->pll_min);
+		results[1] = PLL_FROM_REG(data->pll_max);
+		results[2] = PLL_FROM_REG(data->pll);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->pll_min = PLL_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_PLL_MIN,
+					 data->pll_min);
+		}
+		if (*nrels_mag >= 2) {
+			data->pll_max = PLL_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_PLL_MAX,
+					 data->pll_max);
+		}
+#endif
+	}
+}
+
+void maxi_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_vid(client, operation, ctl_name, nrels_mag,
+			   results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_VID1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 4;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = VID_FROM_REG(data->vid_min[nr - 1]);
+		results[1] = VID_FROM_REG(data->vid_max[nr - 1]);
+		results[2] = VID_FROM_REG(data->vid[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->vid_min[nr - 1] = VID_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_VID_MIN(nr),
+					 data->vid_min[nr - 1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->vid_max[nr - 1] = VID_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_VID_MAX(nr),
+					 data->vid_max[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi99_vid(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr = ctl_name - MAXI_SYSCTL_VID1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 4;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, vid, nr - 1);
+		results[0] = VID99_FROM_REG(nr, data->vid_min[nr - 1]);
+		results[1] = VID99_FROM_REG(nr, data->vid_max[nr - 1]);
+		results[2] = VID99_FROM_REG(nr, data->vid[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		/* still to do */
+		if (*nrels_mag >= 1) {
+			data->vid_min[nr - 1] = VID_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_VID_MIN(nr),
+					 data->vid_min[nr - 1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->vid_max[nr - 1] = VID_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_VID_MAX(nr),
+					 data->vid_max[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi_lcd(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	/* Allows writing and reading from LCD display */
+
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type != nba)
+		return;
+
+	nr = ctl_name - MAXI_SYSCTL_LCD1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = *((long *) &data->lcd[nr - 1][0]);
+		results[1] = *((long *) &data->lcd[nr - 1][4]);
+		results[2] = *((long *) &data->lcd[nr - 1][8]);
+		results[3] = *((long *) &data->lcd[nr - 1][12]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* 
+		   Writing a string to line 3 of the LCD can be done like:
+		   echo -n "Linux MaxiLife" | od -A n -l > \
+		   /proc/sys/dev/sensors/maxilife-nba-i2c-0-14/lcd3
+		 */
+		if (*nrels_mag >= 1)
+			*((long *) &data->lcd[nr - 1][0]) = results[0];
+		if (*nrels_mag >= 2)
+			*((long *) &data->lcd[nr - 1][4]) = results[1];
+		if (*nrels_mag >= 3)
+			*((long *) &data->lcd[nr - 1][8]) = results[2];
+		if (*nrels_mag >= 4)
+			*((long *) &data->lcd[nr - 1][12]) = results[3];
+		maxi_write_token_loop(client, MAXI_TOK_LCD(nr - 1),
+				      strlen(data->lcd[nr - 1]) + 1,
+				      data->lcd[nr - 1]);
+#if 0
+		if (*nrels_mag >= 1)
+			printk("nr=%d, result[0] = %.4s\n", nr,
+			       (char *) &results[0]);
+		if (*nrels_mag >= 2)
+			printk("nr=%d, result[1] = %.4s\n", nr,
+			       (char *) &results[1]);
+		if (*nrels_mag >= 3)
+			printk("nr=%d, result[2] = %.4s\n", nr,
+			       (char *) &results[2]);
+		if (*nrels_mag >= 4)
+			printk("nr=%d, result[3] = %.4s\n", nr,
+			       (char *) &results[3]);
+#endif
+	}
+
+}
+
+void maxi_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (data->type == nba)
+			maxi99_update_client(client, alarm, 0);
+		else
+			maxi_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_maxilife_init(void)
+{
+	printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str,
+	       LM_VERSION, LM_DATE);
+	return i2c_add_driver(&maxi_driver);
+}
+
+static void __exit sm_maxilife_exit(void)
+{
+	i2c_del_driver(&maxi_driver);
+}
+
+
+
+MODULE_AUTHOR("Fons Rademakers <Fons.Rademakers@cern.ch>");
+MODULE_DESCRIPTION("HP MaxiLife driver");
+MODULE_PARM(maxi_version, "i");
+MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version");
+
+module_init(sm_maxilife_init);
+module_exit(sm_maxilife_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/vt1211.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/vt1211.c	(revision 3246)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/vt1211.c	(revision 3246)
@@ -0,0 +1,817 @@
+/*
+    vt1211.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 2002 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.
+*/
+
+/* Supports VIA VT1211 Super I/O sensors via ISA (LPC) accesses only. */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_vid.h"
+
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+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 };
+
+SENSORS_INSMOD_1(vt1211);
+
+/* modified from kernel/include/traps.c */
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+#define PME	0x0b	/* The device with the hardware monitor */
+#define	DEVID	0x20	/* Register: Device ID */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(void)
+{
+	outb(DEV, REG);
+	outb(PME, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x87, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+#define VT1211_DEVID 0x3c
+#define VT1211_ACT_REG 0x30
+#define VT1211_BASE_REG 0x60
+
+#define VT1211_EXTENT 0x80
+
+/* pwm numbered 1-2 */
+#define VT1211_REG_PWM(nr) (0x5f + (nr))
+#define VT1211_REG_PWM_CTL 0x51
+
+/* The VT1211 registers */
+/* We define the sensors as follows. Somewhat convoluted to minimize
+   changes from via686a.
+	Sensor		Voltage Mode	Temp Mode
+	--------	------------	---------
+	Reading 1			temp3   Intel thermal diode
+	Reading 3			temp1   VT1211 internal thermal diode
+	UCH1/Reading2	in0		temp2
+	UCH2		in1		temp4
+	UCH3		in2		temp5
+	UCH4		in3		temp6
+	UCH5		in4		temp7
+	3.3V		in5
+	-12V		in6			not in vt1211
+*/
+
+/* ins numbered 0-6 */
+#define VT1211_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2))
+#define VT1211_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2))
+#define VT1211_REG_IN(nr)     (0x21 + (nr))
+
+/* fans numbered 1-2 */
+#define VT1211_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VT1211_REG_FAN(nr)     (0x28 + (nr))
+
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 };
+
+/* temps numbered 1-7 */
+#define VT1211_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VT1211_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VT1211_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VT1211_REG_TEMP_LOW3	0x4b	/* bits 7-6 */
+#define VT1211_REG_TEMP_LOW2	0x49	/* bits 5-4 */
+#define VT1211_REG_TEMP_LOW47	0x4d
+
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID    0x45
+#define VT1211_REG_FANDIV 0x47
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* temps 1-7; voltages 0-6 */
+#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \
+			      (i) == 3 ? 1 : \
+			      (i) == 2 ? ((ch_config) >> 1) & 0x01 : \
+			                 ((ch_config) >> ((i)-1)) & 0x01)
+#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01))
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255)
+
+#define TEMP_FROM_REG(val) ((val)*10)
+#define TEMP_FROM_REG10(val) (((val)*10)/4)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val)
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255))
+
+
+/********* FAN RPM CONVERSIONS ********/
+/* 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((1310720 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define MIN_TO_REG(a,b) FAN_TO_REG(a,b)
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div)))
+
+struct vt1211_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u16 temp[7];		/* Register value 10 bit */
+	u8 temp_over[7];	/* Register value */
+	u8 temp_hyst[7];	/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding */
+	u8 pwm[2];		/* Register value */
+	u8 pwm_ctl;		/* Register value */
+	u8 vid;			/* Register encoding */
+	u8 vrm;
+	u8 uch_config;
+};
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter);
+static int vt1211_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int vt1211_detach_client(struct i2c_client *client);
+
+static inline int vt_rdval(struct i2c_client *client, u8 register);
+static inline void vt1211_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void vt1211_update_client(struct i2c_client *client);
+static void vt1211_init_client(struct i2c_client *client);
+
+
+static void vt1211_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void vt1211_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void vt1211_in(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_uch(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver vt1211_driver = {
+	.name		= "VT1211 sensors driver",
+	.id		= I2C_DRIVERID_VT1211,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= vt1211_attach_adapter,
+	.detach_client	= vt1211_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define VT1211_SYSCTL_IN0 1000
+#define VT1211_SYSCTL_IN1 1001
+#define VT1211_SYSCTL_IN2 1002
+#define VT1211_SYSCTL_IN3 1003
+#define VT1211_SYSCTL_IN4 1004
+#define VT1211_SYSCTL_IN5 1005
+#define VT1211_SYSCTL_IN6 1006
+#define VT1211_SYSCTL_FAN1 1101
+#define VT1211_SYSCTL_FAN2 1102
+#define VT1211_SYSCTL_TEMP1 1200
+#define VT1211_SYSCTL_TEMP2 1201
+#define VT1211_SYSCTL_TEMP3 1202
+#define VT1211_SYSCTL_TEMP4 1203
+#define VT1211_SYSCTL_TEMP5 1204
+#define VT1211_SYSCTL_TEMP6 1205
+#define VT1211_SYSCTL_TEMP7 1206
+#define VT1211_SYSCTL_VID	1300
+#define VT1211_SYSCTL_PWM1	1401
+#define VT1211_SYSCTL_PWM2	1402
+#define VT1211_SYSCTL_VRM	1600
+#define VT1211_SYSCTL_UCH	1700
+#define VT1211_SYSCTL_FAN_DIV 2000
+#define VT1211_SYSCTL_ALARMS 2001
+
+#define VT1211_ALARM_IN1 0x01
+#define VT1211_ALARM_IN2 0x02
+#define VT1211_ALARM_IN5 0x04
+#define VT1211_ALARM_IN3 0x08
+#define VT1211_ALARM_TEMP1 0x10
+#define VT1211_ALARM_FAN1 0x40
+#define VT1211_ALARM_FAN2 0x80
+#define VT1211_ALARM_IN4 0x100
+#define VT1211_ALARM_IN6 0x200
+#define VT1211_ALARM_TEMP2 0x800
+#define VT1211_ALARM_CHAS 0x1000
+#define VT1211_ALARM_TEMP3 0x8000
+/* duplicates */
+#define VT1211_ALARM_IN0 VT1211_ALARM_TEMP2
+#define VT1211_ALARM_TEMP4 VT1211_ALARM_IN1
+#define VT1211_ALARM_TEMP5 VT1211_ALARM_IN2
+#define VT1211_ALARM_TEMP6 VT1211_ALARM_IN3
+#define VT1211_ALARM_TEMP7 VT1211_ALARM_IN4
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table vt1211_dir_table_template[] = {
+	{VT1211_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+/*
+    datasheet says these are reserved
+	{VT1211_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+*/
+	{VT1211_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP7, "temp7", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan},
+	{VT1211_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan},
+	{VT1211_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan_div},
+	{VT1211_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_alarms},
+	{VT1211_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_pwm},
+	{VT1211_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_pwm},
+	{VT1211_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_vid},
+	{VT1211_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_vrm},
+	{VT1211_SYSCTL_UCH, "uch_config", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_uch},
+	{0}
+};
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, vt1211_detect);
+}
+
+static int __init vt1211_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(VT1211_DEVID != val) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select();
+	val = (superio_inb(VT1211_BASE_REG) << 8) |
+	       superio_inb(VT1211_BASE_REG + 1);
+	*address = val & ~(VT1211_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("vt1211.o: base address not set - use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+int vt1211_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct vt1211_data *data;
+	int err = 0;
+	u8 val;
+	const char *type_name = "vt1211";
+	const char *client_name = "VT1211 chip";
+
+	if (!i2c_is_isa_adapter(adapter)) {
+		return 0;
+	}
+
+	if(force_addr)
+		address = force_addr & ~(VT1211_EXTENT - 1);
+	if (check_region(address, VT1211_EXTENT)) {
+		printk("vt1211.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("vt1211.o: forcing ISA address 0x%04X\n", address);
+		superio_enter();
+		superio_select();
+		superio_outb(VT1211_BASE_REG, address >> 8);
+		superio_outb(VT1211_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	superio_enter();
+	superio_select();
+	if((val = 0x01 & superio_inb(VT1211_ACT_REG)) == 0)
+		superio_outb(VT1211_ACT_REG, 1);
+	superio_exit();
+
+	if (!(data = kmalloc(sizeof(struct vt1211_data), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &vt1211_driver;
+	new_client->flags = 0;
+
+	request_region(address, VT1211_EXTENT, "vt1211-sensors");
+	strcpy(new_client->name, client_name);
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					vt1211_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	vt1211_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, VT1211_EXTENT);
+	kfree(data);
+	return err;
+}
+
+static int vt1211_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct vt1211_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("vt1211.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, VT1211_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+static inline int vt_rdval(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void vt1211_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+static void vt1211_init_client(struct i2c_client *client)
+{
+	struct vt1211_data *data = client->data;
+
+	data->vrm = 91;
+	/* set "default" interrupt mode for alarms, which isn't the default */
+	vt1211_write_value(client, VT1211_REG_TEMP1_CONFIG, 0);
+	vt1211_write_value(client, VT1211_REG_TEMP2_CONFIG, 0);
+}
+
+static void vt1211_update_client(struct i2c_client *client)
+{
+	struct vt1211_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		data->uch_config = vt_rdval(client, VT1211_REG_UCH_CONFIG);
+		for (i = 0; i <= 5; i++) {
+			if(ISVOLT(i, data->uch_config)) {
+				data->in[i] = vt_rdval(client, VT1211_REG_IN(i));
+				data->in_min[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MIN(i));
+				data->in_max[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MAX(i));
+			} else {
+				data->in[i] = 0;
+				data->in_min[i] = 0;
+				data->in_max[i] = 0;
+			}
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+						     VT1211_REG_FAN_MIN(i));
+		}
+		for (i = 1; i <= 7; i++) {
+			if(ISTEMP(i, data->uch_config)) {
+				data->temp[i - 1] = vt_rdval(client,
+					             VT1211_REG_TEMP(i)) << 2;
+				switch(i) {
+					case 1:
+						/* ? */
+						j = 0;
+						break;
+					case 2:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW2) &
+						                    0x30) >> 4;
+						break;
+					case 3:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW3) &
+						                    0xc0) >> 6;
+						break;
+					case 4:
+					case 5:
+					case 6:
+					case 7:
+					default:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW47) >>
+						            ((i-4)*2)) & 0x03;	
+						break;
+	
+				}
+				data->temp[i - 1] |= j;
+				data->temp_over[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_OVER(i));
+				data->temp_hyst[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_HYST(i));
+			} else {
+				data->temp[i - 1] = 0;
+				data->temp_over[i - 1] = 0;
+				data->temp_hyst[i - 1] = 0;
+			}
+		}
+
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+			                                VT1211_REG_FAN_MIN(i));
+			data->pwm[i - 1] = vt_rdval(client, VT1211_REG_PWM(i));
+		}
+
+		data->pwm_ctl = vt_rdval(client, VT1211_REG_PWM_CTL);
+		i = vt_rdval(client, VT1211_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = vt_rdval(client, VT1211_REG_ALARM1) |
+		                    (vt_rdval(client, VT1211_REG_ALARM2) << 8);
+		data->vid= vt_rdval(client, VT1211_REG_VID) & 0x1f;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void vt1211_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			vt1211_write_value(client, VT1211_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			vt1211_write_value(client, VT1211_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void vt1211_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_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] = MIN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr-1]));
+			vt1211_write_value(client, VT1211_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void vt1211_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_TEMP1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_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]);
+			vt1211_write_value(client,
+					    VT1211_REG_TEMP_OVER(nr + 1),
+					    data->temp_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			vt1211_write_value(client,
+					    VT1211_REG_TEMP_HYST(nr + 1),
+					    data->temp_hyst[nr]);
+		}
+	}
+}
+
+void vt1211_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void vt1211_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_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 = vt_rdval(client, VT1211_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);
+			vt1211_write_value(client, VT1211_REG_FANDIV, old);
+		}
+	}
+}
+
+void vt1211_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = 1 + ctl_name - VT1211_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		results[1] = (data->pwm_ctl >> (3 + (4 * (nr - 1)))) & 1;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+			if (*nrels_mag >= 2) {
+				if(results[1]) {
+					data->pwm_ctl |=
+					          (0x08 << (4 * (nr - 1)));
+					vt1211_write_value(client,
+					                   VT1211_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				} else {
+					data->pwm_ctl &=
+					        ~ (0x08 << (4 * (nr - 1)));
+					vt1211_write_value(client,
+					                   VT1211_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				}
+			}
+			vt1211_write_value(client, VT1211_REG_PWM(nr),
+					    data->pwm[nr - 1]);
+		}
+	}
+}
+
+void vt1211_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void vt1211_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void vt1211_uch(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->uch_config & 0x7c;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->uch_config = (data->uch_config & 0x83)|(results[0] & 0x7c);
+			vt1211_write_value(client, VT1211_REG_UCH_CONFIG,
+			                   data->uch_config);
+		}
+	}
+}
+
+static int __init sm_vt1211_init(void)
+{
+	int addr;
+
+	printk("vt1211.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (vt1211_find(&addr)) {
+		printk("vt1211.o: VT1211 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&vt1211_driver);
+}
+
+static void __exit sm_vt1211_exit(void)
+{
+	i2c_del_driver(&vt1211_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_vt1211_init);
+module_exit(sm_vt1211_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/xeontemp.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/xeontemp.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/xeontemp.c	(revision 2867)
@@ -0,0 +1,383 @@
+/*
+    xeontemp.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999,2003  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>, 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.
+*/
+
+/* The Xeon temperature sensor looks just like an ADM1021 with the remote
+   sensor only. There is are no ID registers so detection is difficult. */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+#ifndef I2C_DRIVERID_XEONTEMP
+#define I2C_DRIVERID_XEONTEMP	1045
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x18, 0x1a, 0x29, 0x2b,
+	0x4c, 0x4e, SENSORS_I2C_END
+};
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(xeontemp);
+
+/* xeontemp constants specified below */
+
+/* The registers */
+/* Read-only */
+#define XEONTEMP_REG_REMOTE_TEMP 0x01
+#define XEONTEMP_REG_STATUS 0x02
+/* These use different addresses for reading/writing */
+#define XEONTEMP_REG_CONFIG_R 0x03
+#define XEONTEMP_REG_CONFIG_W 0x09
+#define XEONTEMP_REG_CONV_RATE_R 0x04
+#define XEONTEMP_REG_CONV_RATE_W 0x0A
+/* limits */
+#define XEONTEMP_REG_REMOTE_TOS_R 0x07
+#define XEONTEMP_REG_REMOTE_TOS_W 0x0D
+#define XEONTEMP_REG_REMOTE_THYST_R 0x08
+#define XEONTEMP_REG_REMOTE_THYST_W 0x0E
+/* write-only */
+#define XEONTEMP_REG_ONESHOT 0x0F
+
+#define XEONTEMP_ALARM_RTEMP (XEONTEMP_ALARM_RTEMP_HIGH | XEONTEMP_ALARM_RTEMP_LOW\
+                             | XEONTEMP_ALARM_RTEMP_NA)
+#define XEONTEMP_ALARM_ALL  XEONTEMP_ALARM_RTEMP
+
+/* 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. */
+/* 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))
+
+/* Each client has this additional data */
+struct xeontemp_data {
+	struct i2c_client client;
+	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 remote_temp, remote_temp_os, remote_temp_hyst, alarms;
+	u8 fail;
+};
+
+static int xeontemp_attach_adapter(struct i2c_adapter *adapter);
+static int xeontemp_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static void xeontemp_init_client(struct i2c_client *client);
+static int xeontemp_detach_client(struct i2c_client *client);
+static int xeontemp_read_value(struct i2c_client *client, u8 reg);
+static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask);
+static int xeontemp_write_value(struct i2c_client *client, u8 reg,
+			       u16 value);
+static void xeontemp_remote_temp(struct i2c_client *client, int operation,
+				int ctl_name, int *nrels_mag,
+				long *results);
+static void xeontemp_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void xeontemp_update_client(struct i2c_client *client);
+
+/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
+static int read_only = 0;
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver xeontemp_driver = {
+	.name		= "Xeon temp sensor driver",
+	.id		= I2C_DRIVERID_XEONTEMP,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= xeontemp_attach_adapter,
+	.detach_client	= xeontemp_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define XEONTEMP_SYSCTL_REMOTE_TEMP 1201
+#define XEONTEMP_SYSCTL_ALARMS 1203
+
+#define XEONTEMP_ALARM_RTEMP_HIGH 0x10
+#define XEONTEMP_ALARM_RTEMP_LOW 0x08
+#define XEONTEMP_ALARM_RTEMP_NA 0x04
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected xeontemp. 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 xeontemp_dir_table_template[] = {
+	{XEONTEMP_SYSCTL_REMOTE_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &xeontemp_remote_temp},
+	{XEONTEMP_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &xeontemp_alarms},
+	{0}
+};
+
+static int xeontemp_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, xeontemp_detect);
+}
+
+static int xeontemp_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct xeontemp_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto error0;
+
+	/* 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 xeontemp_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct xeontemp_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto error0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &xeontemp_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    (xeontemp_read_value(new_client, XEONTEMP_REG_STATUS) &
+		     0x03) != 0x00)
+			goto error1;
+	}
+
+	/* Determine the chip type. */
+
+	if (kind <= 0) {
+		kind = xeontemp;
+	}
+
+	type_name = "xeontemp";
+	client_name = "xeon sensors";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,	type_name,
+				    xeontemp_dir_table_template,
+				    THIS_MODULE)) < 0) {
+		err = i;
+		goto error4;
+	}
+	data->sysctl_id = i;
+
+	xeontemp_init_client(new_client);
+	return 0;
+
+      error4:
+	i2c_detach_client(new_client);
+      error3:
+      error1:
+	kfree(data);
+      error0:
+	return err;
+}
+
+static void xeontemp_init_client(struct i2c_client *client)
+{
+	/* Enable ADC and disable suspend mode */
+	xeontemp_write_value(client, XEONTEMP_REG_CONFIG_W, 0);
+	/* Set Conversion rate to 1/sec (this can be tinkered with) */
+	xeontemp_write_value(client, XEONTEMP_REG_CONV_RATE_W, 0x04);
+}
+
+static int xeontemp_detach_client(struct i2c_client *client)
+{
+
+	int err;
+
+	i2c_deregister_entry(((struct xeontemp_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("xeontemp.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+
+}
+
+
+/* All registers are byte-sized */
+static int xeontemp_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* only update value if read succeeded; set fail bit if failed */
+static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask)
+{
+	int i;
+	struct xeontemp_data *data = client->data;
+
+	i = i2c_smbus_read_byte_data(client, reg);
+	if (i < 0) {
+		data->fail |= mask;
+		return i;
+	}
+	*val = i;
+	return 0;
+}
+
+static int xeontemp_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (read_only > 0)
+		return 0;
+
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void xeontemp_update_client(struct i2c_client *client)
+{
+	struct xeontemp_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting xeontemp update\n");
+#endif
+
+		data->fail = 0;
+		xeontemp_rd_good(&(data->remote_temp), client,
+		                XEONTEMP_REG_REMOTE_TEMP, XEONTEMP_ALARM_RTEMP);
+		xeontemp_rd_good(&(data->remote_temp_os), client,
+		                XEONTEMP_REG_REMOTE_TOS_R, XEONTEMP_ALARM_RTEMP);
+		xeontemp_rd_good(&(data->remote_temp_hyst), client,
+		                XEONTEMP_REG_REMOTE_THYST_R,
+		                XEONTEMP_ALARM_RTEMP);
+		data->alarms = XEONTEMP_ALARM_ALL;
+		if (!xeontemp_rd_good(&(data->alarms), client,
+		                     XEONTEMP_REG_STATUS, 0))
+			data->alarms &= XEONTEMP_ALARM_ALL;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void xeontemp_remote_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results)
+{
+	struct xeontemp_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		xeontemp_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_temp_os);
+		results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
+		results[2] = TEMP_FROM_REG(data->remote_temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->remote_temp_os = TEMP_TO_REG(results[0]);
+			xeontemp_write_value(client,
+					    XEONTEMP_REG_REMOTE_TOS_W,
+					    data->remote_temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->remote_temp_hyst = TEMP_TO_REG(results[1]);
+			xeontemp_write_value(client,
+					    XEONTEMP_REG_REMOTE_THYST_W,
+					    data->remote_temp_hyst);
+		}
+	}
+}
+
+void xeontemp_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct xeontemp_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		xeontemp_update_client(client);
+		results[0] = data->alarms | data->fail;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* Can't write to it */
+	}
+}
+
+static int __init sm_xeontemp_init(void)
+{
+	printk(KERN_INFO "xeontemp.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&xeontemp_driver);
+}
+
+static void __exit sm_xeontemp_exit(void)
+{
+	i2c_del_driver(&xeontemp_driver);
+}
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("xeontemp driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(read_only, "i");
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+module_init(sm_xeontemp_init)
+module_exit(sm_xeontemp_exit)
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/smartbatt.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/smartbatt.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/smartbatt.c	(revision 2867)
@@ -0,0 +1,546 @@
+/*
+    smartbatt.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2002  M. 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.
+
+    With GPL code from:
+	   battery.c
+	   Copyright (C) 2000 Linuxcare, Inc. 
+	   battery.h
+	   Copyright (C) 2000 Hypercore Software Design, Ltd.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x0b, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(smartbatt);
+
+/* The SMARTBATT registers */
+#define SMARTBATT_REG_MODE 0x03
+#define SMARTBATT_REG_TEMP 0x08
+#define SMARTBATT_REG_V 0x09
+#define SMARTBATT_REG_I 0x0a
+#define SMARTBATT_REG_AVGI 0x0b
+#define SMARTBATT_REG_RELCHG 0x0d
+#define SMARTBATT_REG_ABSCHG 0x0e
+#define SMARTBATT_REG_REMCAP 0x0f
+#define SMARTBATT_REG_CHGCAP 0x10
+#define SMARTBATT_REG_RUNTIME_E 0x11
+#define SMARTBATT_REG_AVGTIME_E 0x12
+#define SMARTBATT_REG_AVGTIME_F 0x13
+#define SMARTBATT_REG_CHGI 0x14
+#define SMARTBATT_REG_CHGV 0x15
+#define SMARTBATT_REG_STATUS 0x16
+#define SMARTBATT_REG_CYCLECT 0x17
+#define SMARTBATT_REG_DESCAP 0x18
+#define SMARTBATT_REG_DESV 0x19
+#define SMARTBATT_REG_DATE 0x1b
+#define SMARTBATT_REG_SERIAL 0x1c
+#define SMARTBATT_REG_MANUF 0x20
+#define SMARTBATT_REG_NAME 0x21
+#define SMARTBATT_REG_CHEM 0x22
+
+#define BATTERY_STRING_MAX	64
+#define COMM_TIMEOUT 16
+
+
+/* Each client has this additional data */
+struct smartbatt_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+#if 0
+	char manufacturer[BATTERY_STRING_MAX];
+	char device[BATTERY_STRING_MAX];
+	char chemistry[BATTERY_STRING_MAX];
+#endif
+	int  serial;
+	struct {
+		unsigned int day:5;	/* Day (1-31) */
+		unsigned int month:4;	/* Month (1-12) */
+		unsigned int year:7;	/* Year (1980 + 0-127) */
+	} manufacture_date;
+	u16 mode, temp, v, i, avgi;	/* Register values */
+	u16 relchg, abschg, remcap, chgcap;	/* Register values */
+	u16 rte, ate, atf, chgi, chgv;	/* Register values */
+	u16 status, cyclect, descap, desv;	/* Register values */
+};
+
+static int smartbatt_attach_adapter(struct i2c_adapter *adapter);
+static int smartbatt_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static void smartbatt_init_client(struct i2c_client *client);
+static int smartbatt_detach_client(struct i2c_client *client);
+
+static int smartbatt_read(struct i2c_client *client, u8 reg);
+#if 0
+static int smartbatt_write_value(struct i2c_client *client, u8 reg, u16 value);
+#endif
+static void smartbatt_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_i(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_v(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_time(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_alarms(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_charge(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_status(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_error(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void smartbatt_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver smartbatt_driver = {
+	.name		= "Smart Battery chip driver",
+	.id		= I2C_DRIVERID_SMARTBATT,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= smartbatt_attach_adapter,
+	.detach_client	= smartbatt_detach_client,
+};
+
+
+/* -- SENSORS SYSCTL START -- */
+
+/* Status Register Bits */
+/* * * * * * Alarm Bits * * * * */ 
+#define SMARTBATT_OVER_CHARGED_ALARM 0x8000 
+#define SMARTBATT_TERMINATE_CHARGE_ALARM 0x4000 
+#define SMARTBATT_OVER_TEMP_ALARM 0x1000 
+#define SMARTBATT_TERMINATE_DISCHARGE_ALARM 0x0800 
+#define SMARTBATT_REMAINING_CAPACITY_ALARM  0x0200 
+#define SMARTBATT_REMAINING_TIME_ALARM 0x0100 
+/* * * * * * Status Bits * * * * */
+#define SMARTBATT_INITIALIZED 0x0080 
+#define SMARTBATT_DISCHARGING 0x0040 
+#define SMARTBATT_FULLY_CHARGED 0x0020 
+#define SMARTBATT_FULLY_DISCHARGED 0x0010 
+/* * * * * * Error Bits * * * * */ 
+#define SMARTBATT_OK 0x0000 
+#define SMARTBATT_BUSY 0x0001 
+#define SMARTBATT_RESERVED_COMMAND 0x0002 
+#define SMARTBATT_UNSUPPORTED_COMMAND 0x0003 
+#define SMARTBATT_ACCESS_DENIED 0x0004 
+#define SMARTBATT_OVER_UNDERFLOW 0x0005 
+#define SMARTBATT_BAD_SIZE 0x0006 
+#define SMARTBATT_UNKNOWN_ERROR 0x0007
+
+#define SMARTBATT_ALARM (SMARTBATT_OVER_CHARGED_ALARM \
+		| SMARTBATT_TERMINATE_CHARGE_ALARM | SMARTBATT_OVER_TEMP_ALARM \
+		| SMARTBATT_TERMINATE_DISCHARGE_ALARM \
+		| SMARTBATT_REMAINING_CAPACITY_ALARM \
+		| SMARTBATT_REMAINING_TIME_ALARM)
+
+#define SMARTBATT_STATUS (SMARTBATT_INITIALIZED | SMARTBATT_DISCHARGING \
+		| SMARTBATT_FULLY_CHARGED | SMARTBATT_FULLY_DISCHARGED )
+
+#define SMARTBATT_ERROR (SMARTBATT_BUSY | SMARTBATT_RESERVED_COMMAND \
+		| SMARTBATT_UNSUPPORTED_COMMAND | SMARTBATT_ACCESS_DENIED \
+		| SMARTBATT_OVER_UNDERFLOW | SMARTBATT_BAD_SIZE\
+		| SMARTBATT_UNKNOWN_ERROR)
+
+
+#define SMARTBATT_SYSCTL_I 1001
+#define SMARTBATT_SYSCTL_V 1002
+#define SMARTBATT_SYSCTL_TEMP 1003
+#define SMARTBATT_SYSCTL_TIME 1004
+#define SMARTBATT_SYSCTL_ALARMS 1005
+#define SMARTBATT_SYSCTL_STATUS 1006
+#define SMARTBATT_SYSCTL_ERROR 1007
+#define SMARTBATT_SYSCTL_CHARGE 1008
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected SMARTBATT. 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 smartbatt_dir_table_template[] = {
+	{SMARTBATT_SYSCTL_I, "i", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_i},
+	{SMARTBATT_SYSCTL_V, "v", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_v},
+	{SMARTBATT_SYSCTL_TEMP, "temp", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_temp},
+	{SMARTBATT_SYSCTL_TIME, "time", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_time},
+	{SMARTBATT_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_alarms},
+	{SMARTBATT_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_status},
+	{SMARTBATT_SYSCTL_ERROR, "error", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_error},
+	{SMARTBATT_SYSCTL_CHARGE, "charge", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smartbatt_charge},
+	{0}
+};
+
+static int smartbatt_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, smartbatt_detect);
+}
+
+/* This function is called by i2c_detect */
+int smartbatt_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct smartbatt_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("smartbatt.o: smartbatt_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+		    goto ERROR0;
+
+	/* 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 smartbatt_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct smartbatt_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &smartbatt_driver;
+	new_client->flags = 0;
+
+	/* Lousy detection. Check the temp, voltage, and current registers */
+	if (kind < 0) {
+		for (i = 0x08; i <= 0x0a; i++)
+			if (i2c_smbus_read_word_data(new_client, i) == 0xffff)
+				goto ERROR1;
+	}
+
+	kind = smartbatt;
+	type_name = "smartbatt";
+	client_name = "Smart Battery";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					smartbatt_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	smartbatt_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int smartbatt_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct smartbatt_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("smartbatt.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int smartbatt_read(struct i2c_client *client, u8 reg)
+{ 
+	int n = COMM_TIMEOUT;
+	int val;
+	do { 
+		val = i2c_smbus_read_word_data(client, reg);
+	} while ((val == -1) && (n-- > 0));
+	return val;
+}
+
+#if 0
+static int smartbatt_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	/* Why swap bytes? */
+	return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+#endif
+
+#define COMM_TIMEOUT 16
+static void get_battery_info(struct i2c_client *client)
+{
+  struct smartbatt_data *data = client->data;
+  int val;
+
+  down(&data->update_lock);
+  data->chgcap = smartbatt_read(client, SMARTBATT_REG_CHGCAP);
+  data->descap = smartbatt_read(client, SMARTBATT_REG_DESCAP);
+  data->desv = smartbatt_read(client, SMARTBATT_REG_DESV);
+  /* ManufactureDate */
+  val = smartbatt_read(client, SMARTBATT_REG_DATE);
+  data->manufacture_date.day=val & 0x1F;
+  data->manufacture_date.month=(val >> 5) & 0x0F;
+  data->manufacture_date.year=(val >> 9) & 0x7F;
+
+  /* SerialNumber */
+  data->serial = smartbatt_read(client, SMARTBATT_REG_SERIAL);
+#if 0
+  /* ManufacturerName */
+  n=COMM_TIMEOUT;
+  do {
+    val = i2c_smbus_read_block_data(client, 0x20, data->manufacturer);
+  } while ((val == -1) && (n-- > 0));
+  data->manufacturer[val]=0;	
+
+  /* DeviceName */
+  n=COMM_TIMEOUT;
+  do {
+    val = i2c_smbus_read_block_data(client, 0x21, data->device);
+  } while ((val == -1) && (n-- > 0));
+  data->device[val]=0;	
+
+  /* DeviceChemistry */
+  n=COMM_TIMEOUT;
+  do {
+    val = i2c_smbus_read_block_data(client, 0x22, data->chemistry);
+  } while ((val == -1) && (n-- > 0));
+  data->chemistry[val]=0;	
+#endif
+  up(&data->update_lock);
+
+}
+
+static void smartbatt_init_client(struct i2c_client *client)
+{
+	get_battery_info( client );
+}
+
+static void smartbatt_update_client(struct i2c_client *client)
+{
+	struct smartbatt_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		data->mode = smartbatt_read(client, SMARTBATT_REG_MODE);
+		data->temp = smartbatt_read(client, SMARTBATT_REG_TEMP);
+		data->i = smartbatt_read(client, SMARTBATT_REG_I);
+		data->avgi = smartbatt_read(client, SMARTBATT_REG_AVGI);
+		data->v = smartbatt_read(client, SMARTBATT_REG_V);
+		data->chgi = smartbatt_read(client, SMARTBATT_REG_CHGI);
+		data->chgv = smartbatt_read(client, SMARTBATT_REG_CHGV);
+		data->ate = smartbatt_read(client, SMARTBATT_REG_AVGTIME_E);
+		data->atf = smartbatt_read(client, SMARTBATT_REG_AVGTIME_F);
+		data->rte = smartbatt_read(client, SMARTBATT_REG_RUNTIME_E);
+		data->status = smartbatt_read(client, SMARTBATT_REG_STATUS);
+		data->cyclect = smartbatt_read(client, SMARTBATT_REG_CYCLECT);
+		data->relchg = smartbatt_read(client, SMARTBATT_REG_RELCHG);
+		data->abschg = smartbatt_read(client, SMARTBATT_REG_ABSCHG);
+		data->remcap = smartbatt_read(client, SMARTBATT_REG_REMCAP);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void smartbatt_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->temp - 2731; /* convert from Kelvin */
+		*nrels_mag = 1;
+	}
+}
+
+void smartbatt_i(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->chgi;
+		results[1] = data->avgi;
+		results[2] = data->i;
+		*nrels_mag = 3;
+	}
+}
+
+void smartbatt_v(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->chgv;
+		results[1] = data->v;
+		*nrels_mag = 2;
+	}
+}
+
+void smartbatt_time(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->ate;
+		results[1] = data->atf;
+		results[2] = data->rte;
+		*nrels_mag = 3;
+	}
+}
+
+void smartbatt_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->status & SMARTBATT_ALARM;
+		*nrels_mag = 1;
+	}
+}
+
+void smartbatt_status(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->status & SMARTBATT_STATUS;
+		*nrels_mag = 1;
+	}
+}
+
+void smartbatt_error(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->status & SMARTBATT_ERROR;
+		*nrels_mag = 1;
+	}
+}
+
+void smartbatt_charge(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smartbatt_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smartbatt_update_client(client);
+		results[0] = data->relchg;
+		results[1] = data->abschg;
+		results[2] = data->chgi;
+		results[3] = data->chgv;
+		*nrels_mag = 4;
+	}
+}
+
+static int __init sm_smartbatt_init(void)
+{
+	printk("smartbatt.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&smartbatt_driver);
+}
+
+static void __exit sm_smartbatt_exit(void)
+{
+	i2c_del_driver(&smartbatt_driver);
+}
+
+
+
+MODULE_AUTHOR("M. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("Smart Battery driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smartbatt_init);
+module_exit(sm_smartbatt_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/ddcmon.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/ddcmon.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/ddcmon.c	(revision 2867)
@@ -0,0 +1,586 @@
+/*
+    ddcmon.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    and Mark Studebaker <mdsxyz123@yahoo.com>
+    Copyright (c) 2003  Jean Delvare <khali@linux-fr.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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x50, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(ddcmon);
+
+static int checksum = 0;
+MODULE_PARM(checksum, "i");
+MODULE_PARM_DESC(checksum, "Only accept eeproms whose checksum is correct");
+
+/* Many constants specified below */
+
+/* DDCMON registers */
+/* vendor section */
+#define DDCMON_REG_MAN_ID 0x08
+#define DDCMON_REG_PROD_ID 0x0A
+#define DDCMON_REG_SERIAL 0x0C
+#define DDCMON_REG_WEEK 0x10
+#define DDCMON_REG_YEAR 0x11
+/* EDID version */
+#define DDCMON_REG_EDID_VER 0x12
+#define DDCMON_REG_EDID_REV 0x13
+/* display information */
+#define DDCMON_REG_HORSIZE 0x15
+#define DDCMON_REG_VERSIZE 0x16
+#define DDCMON_REG_GAMMA 0x17
+#define DDCMON_REG_DPMS_FLAGS 0x18
+/* supported timings */
+#define DDCMON_REG_ESTABLISHED_TIMINGS 0x23
+#define DDCMON_REG_STANDARD_TIMINGS 0x26
+#define DDCMON_REG_TIMBASE 0x36
+#define DDCMON_REG_TIMINCR 18
+#define DDCMON_REG_TIMNUM   4
+
+#define DDCMON_REG_CHECKSUM 0x7f
+
+/* Size of DDCMON in bytes */
+#define DDCMON_SIZE 128
+
+/* Each client has this additional data */
+struct ddcmon_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 data[DDCMON_SIZE];	/* Register values */
+};
+
+
+static int ddcmon_attach_adapter(struct i2c_adapter *adapter);
+static int ddcmon_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int ddcmon_detach_client(struct i2c_client *client);
+
+static void ddcmon_idcall(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_size(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_sync(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_maxclock(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_timings(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_serial(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_time(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_edid(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_gamma(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_dpms(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_standard_timing(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ddcmon_driver = {
+	.name		= "DDCMON READER",
+	.id		= I2C_DRIVERID_DDCMON,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ddcmon_attach_adapter,
+	.detach_client	= ddcmon_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define DDCMON_SYSCTL_ID 1010
+#define DDCMON_SYSCTL_SIZE 1011
+#define DDCMON_SYSCTL_SYNC 1012
+#define DDCMON_SYSCTL_TIMINGS 1013
+#define DDCMON_SYSCTL_SERIAL 1014
+#define DDCMON_SYSCTL_TIME 1015
+#define DDCMON_SYSCTL_EDID 1016
+#define DDCMON_SYSCTL_GAMMA 1017
+#define DDCMON_SYSCTL_DPMS 1018
+#define DDCMON_SYSCTL_TIMING1 1021
+#define DDCMON_SYSCTL_TIMING2 1022
+#define DDCMON_SYSCTL_TIMING3 1023
+#define DDCMON_SYSCTL_TIMING4 1024
+#define DDCMON_SYSCTL_TIMING5 1025
+#define DDCMON_SYSCTL_TIMING6 1026
+#define DDCMON_SYSCTL_TIMING7 1027
+#define DDCMON_SYSCTL_TIMING8 1028
+#define DDCMON_SYSCTL_MAXCLOCK 1029
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected DDCMON. 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 ddcmon_dir_table_template[] = {
+	{DDCMON_SYSCTL_ID, "id", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ddcmon_idcall},
+	{DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ddcmon_size},
+	{DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_sync},
+	{DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_timings},
+	{DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_serial},
+	{DDCMON_SYSCTL_TIME, "time", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_time},
+	{DDCMON_SYSCTL_EDID, "edid", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_edid},
+	{DDCMON_SYSCTL_GAMMA, "gamma", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_gamma},
+	{DDCMON_SYSCTL_DPMS, "dpms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_dpms},
+	{DDCMON_SYSCTL_TIMING1, "timing1", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING2, "timing2", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING3, "timing3", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING4, "timing4", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING5, "timing5", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING6, "timing6", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING7, "timing7", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING8, "timing8", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_MAXCLOCK, "maxclock", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_maxclock},
+	{0}
+};
+
+static int ddcmon_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, ddcmon_detect);
+}
+
+/* This function is called by i2c_detect */
+int ddcmon_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i, cs;
+	struct i2c_client *new_client;
+	struct ddcmon_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		    goto ERROR0;
+
+	/* 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 ddcmon_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct ddcmon_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	memset(data->data, 0xff, DDCMON_SIZE);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ddcmon_driver;
+	new_client->flags = 0;
+
+	/* prevent 24RF08 corruption (just in case) */
+	i2c_smbus_write_quick(new_client, 0);
+
+	/* Now, we do the remaining detection. */
+	if (checksum) {
+		int cs = 0;
+		for (i = 0; i < 0x80; i++)
+			cs += i2c_smbus_read_byte_data(new_client, i);
+		if ((cs & 0xff) != 0)
+			goto ERROR1;
+	}
+
+	/* Verify the first 8 locations 0x00FFFFFFFFFFFF00 */
+	/* Allow force and force_ddcmon arguments */
+	if(kind < 0)
+	{
+		for(i = 0; i < 8; i++) {
+			cs = i2c_smbus_read_byte_data(new_client, i);
+			if(i == 0 || i == 7) {
+				if(cs != 0)
+					goto ERROR1;
+			} else if(cs != 0xff)
+				goto ERROR1;
+		}
+	}
+
+	type_name = "ddcmon";
+	client_name = "DDC Monitor";
+
+	/* Fill in the remaining client fields and put it in the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					ddcmon_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int ddcmon_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct ddcmon_data *) (client->data))->
+				 sysctl_id);
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("ddcmon.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+	return 0;
+}
+
+static void ddcmon_update_client(struct i2c_client *client)
+{
+	struct ddcmon_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 300 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		if (i2c_check_functionality(client->adapter,
+		                            I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		{
+			for (i=0; i<DDCMON_SIZE; i+=I2C_SMBUS_I2C_BLOCK_MAX)
+				if (i2c_smbus_read_i2c_block_data(client,
+				                           i, data->data + i)
+				                    != I2C_SMBUS_I2C_BLOCK_MAX) {
+					printk(KERN_WARNING "ddcmon.o: block read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+		} else {
+			if (i2c_smbus_write_byte(client, 0)) {
+				printk(KERN_WARNING "ddcmon.o: read start fail at 0!\n");
+				goto DONE;
+			}
+			for (i = 0; i < DDCMON_SIZE; i++) {
+				j = i2c_smbus_read_byte(client);
+				if (j < 0) {
+					printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+				data->data[i] = (u8) j;
+			}
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+DONE:
+	up(&data->update_lock);
+}
+
+
+void ddcmon_idcall(struct i2c_client *client, int operation,
+		   int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_MAN_ID + 1] |
+		             (data->data[DDCMON_REG_MAN_ID] << 8);
+		results[1] = data->data[DDCMON_REG_PROD_ID + 1] |
+		             (data->data[DDCMON_REG_PROD_ID] << 8);
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_size(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_VERSIZE];
+		results[1] = data->data[DDCMON_REG_HORSIZE];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_sync(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	int i, j;
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		*nrels_mag = 4;
+		/* look for monitor limits entry */
+		for(i = DDCMON_REG_TIMBASE;
+		    i < DDCMON_REG_TIMBASE +
+		        (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR);
+		    i += DDCMON_REG_TIMINCR) {
+			if (data->data[i] == 0x00
+			 && data->data[i + 1] == 0x00
+			 && data->data[i + 2] == 0x00
+			 && data->data[i + 3] == 0xfd) {
+				for(j = 0; j < 4; j++)
+					results[j] = data->data[i + j + 5];
+				return;
+			}
+		}
+		for(j = 0; j < 4; j++)
+			results[j] = 0;
+	}
+}
+
+void ddcmon_maxclock(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		*nrels_mag = 1;
+		/* look for monitor limits entry */
+		for(i = DDCMON_REG_TIMBASE;
+		    i < DDCMON_REG_TIMBASE +
+		        (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR);
+		    i += DDCMON_REG_TIMINCR) {
+			if (data->data[i] == 0x00
+			 && data->data[i + 1] == 0x00
+			 && data->data[i + 2] == 0x00
+			 && data->data[i + 3] == 0xfd) {
+				results[0] = (data->data[i + 9] == 0xff ?
+				             0 : data->data[i + 9] * 10);
+				return;
+			}
+		}
+		results[0] = 0;
+	}
+}
+
+void ddcmon_timings(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_ESTABLISHED_TIMINGS] |
+		             (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 1] << 8) |
+		             (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 2] << 16);
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_serial(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_SERIAL] |
+		             (data->data[DDCMON_REG_SERIAL + 1] << 8) |
+		             (data->data[DDCMON_REG_SERIAL + 2] << 16) |
+		             (data->data[DDCMON_REG_SERIAL + 3] << 24);
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_time(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_YEAR] + 1990;
+		results[1] = data->data[DDCMON_REG_WEEK];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_edid(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_EDID_VER];
+		results[1] = data->data[DDCMON_REG_EDID_REV];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_gamma(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = 100 + data->data[DDCMON_REG_GAMMA];
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_dpms(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_DPMS_FLAGS];
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_standard_timing(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+	int nr = ctl_name - DDCMON_SYSCTL_TIMING1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		/* If both bytes of the timing are 0x00 or 0x01, then the timing
+		   slot is unused. */
+		if ((data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2]
+		    | data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1]) & 0xfe) {
+			results[0] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2] + 31) * 8;
+			switch (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] >> 6) {
+				/* We don't care about rounding issues there, it really
+				   should be OK without it. */
+				case 0x00:
+					results[1] = results[0]; /* unconfirmed */
+					break;
+				case 0x01:
+					results[1] = results[0] * 3 / 4;
+					break;
+				case 0x02:
+					results[1] = results[0] * 4 / 5;
+					break;
+				case 0x03:
+					results[1] = results[0] * 9 / 16;
+					break;
+			}
+			results[2] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] & 0x3f) + 60;
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	}
+}
+
+static int __init sm_ddcmon_init(void)
+{
+	printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ddcmon_driver);
+}
+
+static void __exit sm_ddcmon_exit(void)
+{
+	i2c_del_driver(&ddcmon_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "Mark Studebaker <mdsxyz123@yahoo.com> "
+		  "and Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("DDCMON driver");
+
+module_init(sm_ddcmon_init);
+module_exit(sm_ddcmon_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/adm1031.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/adm1031.c	(revision 3017)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/adm1031.c	(revision 3017)
@@ -0,0 +1,643 @@
+/*
+  adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+  Based on lm75.c and lm85.c
+  Supports Analog Devices ADM1030 and ADM1031
+  Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org> and
+                     Jean Delvare <khali@linux-fr.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.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include "version.h"
+
+/* Following macros take channel parameter starting from 0 */
+#define ADM1031_REG_FAN_SPEED(nr)	(0x08 + (nr))
+
+#define ADM1031_REG_FAN_DIV(nr)		(0x20 + (nr))
+#define ADM1031_REG_FAN_MIN(nr)		(0x10 + (nr))
+
+#define ADM1031_REG_TEMP_MAX(nr)	(0x14 + 4 * (nr))
+#define ADM1031_REG_TEMP_MIN(nr)	(0x15 + 4 * (nr))
+#define ADM1031_REG_TEMP_CRIT(nr)	(0x16 + 4 * (nr))
+
+#define ADM1031_REG_TEMP(nr)		(0x0a + (nr))
+#define ADM1031_REG_AUTO_TEMP(nr)	(0x24 + (nr))
+
+#define ADM1031_REG_STATUS(nr)		(0x02 + (nr))
+#define ADM1031_REG_FAN_PWM		0x22
+
+#define ADM1031_REG_CONF1		0x00
+#define ADM1031_REG_CONF2		0x01
+#define ADM1031_REG_EXT_TEMP		0x06
+
+#define ADM1031_CONF1_MONITOR_ENABLE	0x01 /* Monitoring enable */
+#define ADM1031_CONF1_PWM_INVERT	0x08 /* PWM Invert (unused) */
+#define ADM1031_CONF1_AUTO_MODE		0x80 /* Auto fan mode */
+
+#define ADM1031_CONF2_PWM1_ENABLE	0x01
+#define ADM1031_CONF2_PWM2_ENABLE	0x02
+#define ADM1031_CONF2_TACH1_ENABLE	0x04
+#define ADM1031_CONF2_TACH2_ENABLE	0x08
+#define ADM1031_CONF2_TEMP_ENABLE(chan)	(0x10 << (chan))
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(adm1030, adm1031);
+
+/*
+ * Proc entries
+ * These files are created for each detected ADM1031.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define ADM1031_SYSCTL_TEMP1		1200
+#define ADM1031_SYSCTL_TEMP2		1201
+#define ADM1031_SYSCTL_TEMP3		1202
+
+#define ADM1031_SYSCTL_FAN1		1210
+#define ADM1031_SYSCTL_FAN2		1211
+
+#define ADM1031_SYSCTL_FAN_DIV		1220
+
+#define ADM1031_SYSCTL_ALARMS		1250
+
+#define ADM1031_ALARM_FAN1_MIN		0x0001
+#define ADM1031_ALARM_FAN1_FLT		0x0002
+#define ADM1031_ALARM_TEMP2_HIGH	0x0004
+#define ADM1031_ALARM_TEMP2_LOW		0x0008
+#define ADM1031_ALARM_TEMP2_CRIT	0x0010
+#define ADM1031_ALARM_TEMP2_DIODE	0x0020
+#define ADM1031_ALARM_TEMP1_HIGH	0x0040
+#define ADM1031_ALARM_TEMP1_LOW		0x0080
+#define ADM1031_ALARM_FAN2_MIN		0x0100
+#define ADM1031_ALARM_FAN2_FLT		0x0200
+#define ADM1031_ALARM_TEMP3_HIGH	0x0400
+#define ADM1031_ALARM_TEMP3_LOW		0x0800
+#define ADM1031_ALARM_TEMP3_CRIT	0x1000
+#define ADM1031_ALARM_TEMP3_DIODE	0x2000
+#define ADM1031_ALARM_TEMP1_CRIT	0x4000
+#define ADM1031_ALARM_THERMAL		0x8000
+
+
+/* -- SENSORS SYSCTL END -- */
+static void adm1031_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm1031_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1031_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1031_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+static ctl_table adm1031_dir_table_template[] = {
+	{ADM1031_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_temp},
+	{ADM1031_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_temp},
+	{ADM1031_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_temp},
+	{ADM1031_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_fan},
+	{ADM1031_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_fan_div},
+	{ADM1031_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_fan},
+	{ADM1031_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_alarms},
+	{0}
+};
+
+static ctl_table adm1030_dir_table_template[] = {
+	{ADM1031_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_temp},
+	{ADM1031_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_temp},
+	{ADM1031_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_fan},
+	{ADM1031_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_fan_div},
+	{ADM1031_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1031_alarms},
+	{0}
+};
+
+
+/* Each client has this additional data */
+struct adm1031_data {
+	struct i2c_client client;
+	int sysctl_id;
+	struct semaphore update_lock;
+	int chip_type;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 alarms;
+	u8 conf1;
+	u8 conf2;
+	u8 fan[2];
+	u8 fan_min[2];
+	u8 fan_pwm[2];
+	u8 fan_div[2];
+	s8 temp[3];
+	u8 auto_temp[3];
+	u8 ext_temp[3];
+	s8 temp_min[3];
+	s8 temp_max[3];
+	s8 temp_crit[3];
+};
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter);
+static int adm1031_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static void adm1031_init_client(struct i2c_client *client);
+static int adm1031_detach_client(struct i2c_client *client);
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg);
+static inline int adm1031_write_value(struct i2c_client *client, u8 reg,
+				      unsigned int value);
+
+static void adm1031_update_client(struct i2c_client *client);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver adm1031_driver = {
+	.name		= "ADM1031/ADM1030 sensor driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm1031_attach_adapter,
+	.detach_client	= adm1031_detach_client,
+};
+
+#define TEMP_TO_REG(val)		((val) < 0 ? (((val) - 500) / 1000) : \
+					 (((val) + 500) / 1000))
+
+#define TEMP_FROM_REG(reg)		((reg) * 1000)
+
+#define TEMP_FROM_REG_EXT(val,ext)	(TEMP_FROM_REG(val) + (ext) * 125)
+
+#define FAN_FROM_REG(reg,div)		((reg) ? 675000 / ((reg) * (div)) : 0)
+
+#define FAN_TO_REG(val,div)		((val) <= 0 ? 0 : \
+					 (val) * (div) >= 675000 ? 1 : \
+					 (val) * (div) <= 2647 ? 255 : \
+					 675000 / ((val) * (div)))
+
+#define FAN_DIV_TO_REG(val)		((val) == 8 ? 0xc0 : \
+					 (val) == 4 ? 0x80 : \
+					 (val) == 1 ? 0x00 : 0x40)
+
+#define FAN_DIV_FROM_REG(reg)		(1 << (((reg)&0xc0) >> 6))
+
+#define AUTO_TEMP_MIN_FROM_REG(reg)	(((reg) >> 3) * 4)
+
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm1031_detect);
+}
+
+/* This function is called by i2c_detect */
+static int
+adm1031_detect(struct i2c_adapter *adapter, int address,
+	       unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct adm1031_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(data, 0, sizeof(struct adm1031_data));
+
+	new_client = &data->client;
+	new_client->data = data;
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1031_driver;
+	new_client->flags = 0;
+
+	if (kind < 0) {
+		int id, co;
+		id = i2c_smbus_read_byte_data(new_client, 0x3d);
+		co = i2c_smbus_read_byte_data(new_client, 0x3e);
+
+		if (((id != 0x31) || (id != 0x30)) && (co != 0x41))
+			goto exit_free;
+		kind = (id == 0x30) ? adm1030 : adm1031;
+	}
+
+	if (kind <= 0)
+		kind = adm1031;
+
+	/* Given the detected chip type, set the chip name and the
+	 * auto fan control helper table. */
+	if (kind == adm1030) {
+		type_name = "adm1030";
+		client_name = "ADM1030 chip";
+
+	} else if (kind == adm1031) {
+		type_name = "adm1031";
+		client_name = "ADM1031 chip";
+	}
+	data->chip_type = kind;
+
+	strcpy(new_client->name, client_name);
+	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 exit_free;
+
+
+	if (kind == adm1030) {
+		if ((err = i2c_register_entry(new_client, type_name,
+					      adm1030_dir_table_template,
+					      THIS_MODULE)) < 0)
+			goto exit_detach;
+	} else if (kind == adm1031) {
+		if ((err = i2c_register_entry(new_client, type_name,
+					      adm1031_dir_table_template,
+					      THIS_MODULE)) < 0)
+			goto exit_detach;
+	}
+
+	data->sysctl_id = err;
+
+	/* Initialize the ADM1031 chip */
+	adm1031_init_client(new_client);
+
+	return 0;
+
+exit_detach:
+	i2c_detach_client(new_client);
+exit_free:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int adm1031_detach_client(struct i2c_client *client)
+{
+	int ret;
+	struct adm1031_data *data = client->data;
+
+	i2c_deregister_entry(data->sysctl_id);
+	if ((ret = i2c_detach_client(client)) != 0) {
+		return ret;
+	}
+
+	kfree(data);
+	return 0;
+}
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int adm1031_write_value(struct i2c_client *client, u8 reg,
+				      unsigned int value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void adm1031_init_client(struct i2c_client *client)
+{
+	unsigned int read_val;
+	unsigned int mask;
+	struct adm1031_data *data = client->data;
+
+	mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
+	if (data->chip_type == adm1031) {
+		mask |= (ADM1031_CONF2_PWM2_ENABLE |
+			 ADM1031_CONF2_TACH2_ENABLE);
+	}
+	/* Initialize the ADM1031 chip (enable fan speed reading) */
+	read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
+	if ((read_val | mask) != read_val) {
+		adm1031_write_value(client, ADM1031_REG_CONF2,
+				    read_val | mask);
+	}
+
+	read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
+	if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
+		adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
+				    ADM1031_CONF1_MONITOR_ENABLE);
+	}
+}
+
+static void adm1031_update_client(struct i2c_client *client)
+{
+	struct adm1031_data *data = client->data;
+	int chan;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk(KERN_INFO "adm1031.o: Starting chip update\n");
+#endif
+		for (chan = 0;
+		     chan < ((data->chip_type == adm1031) ? 3 : 2);
+		     chan++) {
+			u8 oldh, newh;
+
+			oldh = adm1031_read_value(client,
+						  ADM1031_REG_TEMP(chan));
+			data->ext_temp[chan] = adm1031_read_value(client,
+							ADM1031_REG_EXT_TEMP);
+			newh = adm1031_read_value(client,
+						  ADM1031_REG_TEMP(chan));
+			if (newh != oldh) {
+				data->ext_temp[chan] = adm1031_read_value(client,
+							ADM1031_REG_EXT_TEMP);
+#ifdef DEBUG
+				oldh = adm1031_read_value(client,
+							  ADM1031_REG_TEMP(chan));
+
+				/* oldh is actually newer */
+				if (newh != oldh)
+					printk(KERN_INFO "adm1031.o: Remote "
+					       "temperature may be wrong.\n");
+#endif
+			}
+			data->temp[chan] = newh;
+
+			data->temp_min[chan] = adm1031_read_value(client,
+					       ADM1031_REG_TEMP_MIN(chan));
+			data->temp_max[chan] = adm1031_read_value(client,
+					       ADM1031_REG_TEMP_MAX(chan));
+			data->temp_crit[chan] = adm1031_read_value(client,
+						ADM1031_REG_TEMP_CRIT(chan));
+			data->auto_temp[chan] = adm1031_read_value(client,
+						ADM1031_REG_AUTO_TEMP(chan));
+		}
+
+		data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
+
+		data->alarms = adm1031_read_value(client, ADM1031_REG_STATUS(0))
+			     | (adm1031_read_value(client, ADM1031_REG_STATUS(1))
+				<< 8);
+		if (data->chip_type == adm1030) {
+			data->alarms &= 0xc0ff;
+		}
+
+		for (chan = 0;
+		     chan < (data->chip_type == adm1030 ? 1 : 2);
+		     chan++) {
+			data->fan_div[chan] = adm1031_read_value(client,
+					      ADM1031_REG_FAN_DIV(chan));
+			data->fan_min[chan] = adm1031_read_value(client,
+					      ADM1031_REG_FAN_MIN(chan));
+			data->fan[chan] = adm1031_read_value(client,
+					  ADM1031_REG_FAN_SPEED(chan));
+			data->fan_pwm[chan] = (adm1031_read_value(client,
+					       ADM1031_REG_FAN_PWM) >> (4 * chan))
+					      & 0x0f;
+		}
+
+		data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
+		data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static int __init sensors_adm1031_init(void)
+{
+	printk(KERN_INFO "adm1031.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&adm1031_driver);
+}
+
+static void __exit sensors_adm1031_exit(void)
+{
+	i2c_del_driver(&adm1031_driver);
+}
+
+
+static void
+adm1031_temp(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	int ext;
+	struct adm1031_data *data = client->data;
+	int tmp;
+	int nr = ctl_name - ADM1031_SYSCTL_TEMP1;
+
+	ext = nr == 0 ?
+		((data->ext_temp[nr] >> 6) & 0x3) * 2 :
+		(((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1031_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_max[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_min[nr]);
+		results[2] = TEMP_FROM_REG(data->temp_crit[nr]);
+		results[3] = TEMP_FROM_REG_EXT(data->temp[nr], ext);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			tmp = SENSORS_LIMIT(results[0], -55000, 127000);
+			data->temp_max[nr] = TEMP_TO_REG(tmp);
+			adm1031_write_value(client,
+					    ADM1031_REG_TEMP_MAX(nr),
+					    data->temp_max[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			tmp = SENSORS_LIMIT(results[1], -55000, 127000);
+			data->temp_min[nr] = TEMP_TO_REG(tmp);
+			adm1031_write_value(client,
+					    ADM1031_REG_TEMP_MIN(nr),
+					    data->temp_min[nr]);
+		}
+		if (*nrels_mag >= 3) {
+			tmp = SENSORS_LIMIT(results[2], -55000, 127000);
+			data->temp_crit[nr] = TEMP_TO_REG(tmp);
+			adm1031_write_value(client,
+					    ADM1031_REG_TEMP_CRIT(nr),
+					    data->temp_crit[nr]);
+		}
+	}
+}
+
+/*
+ * That function checks the cases where the fan reading is not
+ * relevant.  It is used to provide 0 as fan reading when the fan is
+ * not supposed to run.
+ */
+static int trust_fan_readings(struct adm1031_data * data, int chan)
+{
+	int res = 0;
+
+	if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
+		switch(data->conf1 & 0x60) {
+		case 0x00: /* temp2 controls fan1, temp3 controls fan2 */
+			res = data->temp[chan+1] >=
+			      AUTO_TEMP_MIN_FROM_REG(data->auto_temp[chan+1]);
+			break;
+		case 0x20: /* temp2 controls both fans */
+			res = data->temp[1] >=
+			      AUTO_TEMP_MIN_FROM_REG(data->auto_temp[1]);
+			break;
+		case 0x40: /* temp3 controls both fans */
+			res = data->temp[2] >=
+			      AUTO_TEMP_MIN_FROM_REG(data->auto_temp[2]);
+			break;
+		case 0x60: /* max of temp1, temp2 and temp3 controls both
+			      fans */
+			res = data->temp[0] >=
+			      AUTO_TEMP_MIN_FROM_REG(data->auto_temp[0])
+			      || data->temp[1] >=
+			      AUTO_TEMP_MIN_FROM_REG(data->auto_temp[1])
+			      || (data->chip_type == adm1031
+				  && data->temp[2] >=
+				  AUTO_TEMP_MIN_FROM_REG(data->auto_temp[2]));
+			break;
+		}
+	} else {
+		res = data->fan_pwm[chan] > 0;
+	}
+
+	return res;
+}
+
+static void adm1031_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1031_data *data = client->data;
+	int nr = ctl_name - ADM1031_SYSCTL_FAN1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+	        adm1031_update_client(client);
+	        results[0] = FAN_FROM_REG(data->fan_min[nr],
+			     FAN_DIV_FROM_REG(data->fan_div[nr]));
+		results[1] = trust_fan_readings(data, nr) ?
+			     FAN_FROM_REG(data->fan[nr],
+			     FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+					    FAN_DIV_FROM_REG(data->fan_div[nr]));
+			adm1031_write_value(client,
+					    ADM1031_REG_FAN_MIN(nr),
+					    data->fan_min[nr]);
+		}
+	}
+}
+
+static void adm1031_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1031_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1031_update_client(client);
+		results[0] = FAN_DIV_FROM_REG(data->fan_div[0]);
+		*nrels_mag = 1;
+		if(data->chip_type == adm1031) {
+		    results[1] = FAN_DIV_FROM_REG(data->fan_div[1]);
+		    *nrels_mag = 2;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		int old_div, new_min;
+		if (*nrels_mag >= 1) {
+			old_div = FAN_DIV_FROM_REG(data->fan_div[0]);
+			data->fan_div[0] = FAN_DIV_TO_REG(results[0]);
+			adm1031_write_value(client,
+					    ADM1031_REG_FAN_DIV(0),
+					    data->fan_div[0]);
+			new_min = data->fan_min[0] * old_div /
+				  FAN_DIV_FROM_REG(data->fan_div[0]);
+			data->fan_min[0] = new_min > 0xff ? 0xff : new_min;
+			adm1031_write_value(client,
+					    ADM1031_REG_FAN_MIN(0),
+					    data->fan_min[0]);
+		}
+		if (*nrels_mag >= 2) {
+			old_div = FAN_DIV_FROM_REG(data->fan_div[1]);
+			data->fan_div[1] = FAN_DIV_TO_REG(results[1]);
+			adm1031_write_value(client,
+					    ADM1031_REG_FAN_DIV(1),
+					    data->fan_div[1]);
+			new_min = data->fan_min[1] * old_div /
+				  FAN_DIV_FROM_REG(data->fan_div[1]);
+			data->fan_min[1] = new_min > 0xff ? 0xff : new_min;
+			adm1031_write_value(client,
+					    ADM1031_REG_FAN_MIN(1),
+					    data->fan_min[1]);
+		}
+	}
+}
+
+static void adm1031_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1031_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1031_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
+MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_adm1031_init);
+module_exit(sensors_adm1031_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/max1619.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/max1619.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/max1619.c	(revision 2867)
@@ -0,0 +1,491 @@
+/*
+ * max1619.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring
+ * Copyright (C) 2004 Alexey Fisher <fishor@mail.ru>
+ *                    Jean Delvare <khali@linux-fr.org>
+ *
+ * Copied from lm90.c:
+ * Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
+ *
+ * The MAX1619 is a sensor chip made by Maxim. It reports up to two
+ * temperatures (its own plus up to one external one).
+ * Complete datasheet can be obtained from Maxim's website at:
+ *   http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+/*
+ * Addresses to scan
+ */
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = {0x18, 0x1a, 0x29, 0x2b,
+	0x4c, 0x4e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(max1619);
+
+/*
+ * The max1619 registers
+ */
+
+#define MAX1619_REG_R_CONFIG		0x03
+#define MAX1619_REG_W_CONFIG		0x09
+#define MAX1619_REG_R_CONVRATE		0x04
+#define MAX1619_REG_W_CONVRATE		0x0A
+#define MAX1619_REG_R_STATUS		0x02
+#define MAX1619_REG_R_LOCAL_TEMP	0x00
+#define MAX1619_REG_R_REMOTE_TEMP	0x01
+#define MAX1619_REG_R_REMOTE_THIGH	0x07
+#define MAX1619_REG_W_REMOTE_THIGH	0x0d
+#define MAX1619_REG_R_REMOTE_TLOW	0x08
+#define MAX1619_REG_W_REMOTE_TLOW	0x0E
+#define MAX1619_REG_R_REMOTE_TMAX	0x10
+#define MAX1619_REG_W_REMOTE_TMAX	0x12
+#define MAX1619_REG_R_REMOTE_THYST	0x11
+#define MAX1619_REG_W_REMOTE_THYST	0x13
+#define MAX1619_REG_R_MAN_ID		0xFE
+#define MAX1619_REG_R_CHIP_ID		0xFF
+
+/*
+ * Conversions and various macros
+ */
+
+#define TEMP_FROM_REG(val)	((val) & 0x80 ? (val)-0x100 : (val))
+#define TEMP_TO_REG(val)	((val) < 0 ? (val)+0x100 : (val))
+
+/*
+ * Functions declaration
+ */
+
+static int max1619_attach_adapter(struct i2c_adapter *adapter);
+static int max1619_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind);
+static void max1619_init_client(struct i2c_client *client);
+static int max1619_detach_client(struct i2c_client *client);
+static void max1619_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void max1619_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void max1619_remote_crit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void max1619_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver max1619_driver = {
+	.name		= "MAX1619 sensor driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= max1619_attach_adapter,
+	.detach_client	= max1619_detach_client
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max1619_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* register values */
+	u8 local_temp;
+	u8 remote_temp, remote_high, remote_low;
+	u8 remote_hyst, remote_max;
+	u8 alarms;
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected max1619.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define MAX1619_SYSCTL_LOCAL_TEMP	1200
+#define MAX1619_SYSCTL_REMOTE_TEMP	1201
+#define MAX1619_SYSCTL_REMOTE_CRIT	1202
+#define MAX1619_SYSCTL_ALARMS		1203
+
+#define MAX1619_ALARM_REMOTE_THIGH	0x10
+#define MAX1619_ALARM_REMOTE_TLOW	0x08
+#define MAX1619_ALARM_REMOTE_OPEN	0x04
+#define MAX1619_ALARM_REMOTE_OVERT	0x02
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table max1619_dir_table_template[] =
+{
+	{MAX1619_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max1619_local_temp},
+	{MAX1619_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max1619_remote_temp},
+	{MAX1619_SYSCTL_REMOTE_CRIT,"temp2_crit", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max1619_remote_crit},
+	{MAX1619_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max1619_alarms},
+	{0}
+};
+
+/*
+ * Real code
+ */
+
+static int max1619_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, max1619_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int max1619_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct max1619_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk(KERN_DEBUG "max1619.o: Called for an ISA bus "
+		       "adapter, aborting.\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "max1619.o: I2C bus doesn't support "
+		       "byte read mode, skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) {
+		printk(KERN_ERR "max1619.o: Out of memory in "
+		       "max1619_detect (new_client).\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right before the
+	 * MAX1619-specific data. The MAX1619-specific data is pointed to
+	 * by the data field from the I2C client da1ta.
+	 */
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &max1619_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip. A zero kind means that
+	 * the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+
+	if (kind < 0) {
+		u8 reg_config, reg_convrate, reg_status;
+
+		reg_config = i2c_smbus_read_byte_data(new_client,
+			MAX1619_REG_R_CONFIG);
+		reg_convrate = i2c_smbus_read_byte_data(new_client,
+			MAX1619_REG_R_CONVRATE);
+		reg_status = i2c_smbus_read_byte_data(new_client,
+			MAX1619_REG_R_STATUS);
+
+		if ((reg_config & 0x03) != 0x00
+		 || reg_convrate > 0x07
+		 || (reg_status & 0x61) != 0x00) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "max1619.o: Detection failed at "
+			       "0x%02x.\n", address);
+#endif
+			goto ERROR1;
+		}
+	}
+
+	if (kind <= 0) {
+		u8 man_id, chip_id;
+
+		man_id = i2c_smbus_read_byte_data(new_client,
+			MAX1619_REG_R_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			MAX1619_REG_R_CHIP_ID);
+
+		if ((man_id == 0x4D) && (chip_id == 0x04)) {
+			kind = max1619;
+		}
+	}
+
+	if (kind <= 0) {
+		printk(KERN_INFO "max1619.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == max1619) {
+		type_name = "max1619";
+		client_name = "MAX1619 chip";
+	} else {
+		printk(KERN_ERR "max1619.o: Unknown kind %d.\n", kind);
+		goto ERROR1;
+	}
+
+	/*
+	 * OK, we got a valid chip so we can fill in the remaining client
+	 * fields.
+	 */
+
+	strcpy(new_client->name, client_name);
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer a new client has arrived.
+	 */
+
+	if ((err = i2c_attach_client(new_client))) {
+		printk(KERN_ERR "max1619.o: Failed attaching client.\n");
+		goto ERROR1;
+	}
+
+	/*
+	 * Register a new directory entry.
+	 */
+
+	if ((err = i2c_register_entry(new_client, type_name,
+	     max1619_dir_table_template, THIS_MODULE)) < 0) {
+		printk(KERN_ERR "max1619.o: Failed registering directory "
+		       "entry.\n");
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+	/*
+	 * Initialize the MAX1619 chip.
+	 */
+
+	max1619_init_client(new_client);
+	return 0;
+
+ERROR2:
+	i2c_detach_client(new_client);
+ERROR1:
+	kfree(data);
+	return err;
+}
+
+static void max1619_init_client(struct i2c_client *client)
+{
+	u8 config;
+
+	/*
+	 * Start the conversions.
+	 */
+
+	/* Set conversion rate to 2 Hz */
+	i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, 5);
+
+	/* Start monitoring */
+	config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+	if (config & 0x40)
+		i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
+			config & 0xBF);
+}
+
+
+static int max1619_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct max1619_data *)
+		(client->data))->sysctl_id);
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "max1619.o: Client deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+static void max1619_update_client(struct i2c_client *client)
+{
+	struct max1619_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "max1619.o: Updating data.\n");
+#endif
+
+		data->local_temp = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_LOCAL_TEMP);
+		data->remote_temp = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_REMOTE_TEMP);
+		data->remote_high = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_REMOTE_THIGH);
+		data->remote_low = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_REMOTE_TLOW);
+		data->remote_max = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_REMOTE_TMAX);
+		data->remote_hyst = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_REMOTE_THYST);
+		data->alarms = i2c_smbus_read_byte_data(client,
+			MAX1619_REG_R_STATUS);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void max1619_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct max1619_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		max1619_update_client(client);
+		results[0] = TEMP_FROM_REG(data->local_temp);
+		*nrels_mag = 1;
+	}
+
+}
+
+static void max1619_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct max1619_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		max1619_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_high);
+		results[1] = TEMP_FROM_REG(data->remote_low);
+		results[2] = TEMP_FROM_REG(data->remote_temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->remote_high = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client,
+				MAX1619_REG_W_REMOTE_THIGH, data->remote_high);
+		}
+		if (*nrels_mag >= 2) {
+			data->remote_low = TEMP_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client,
+				MAX1619_REG_W_REMOTE_TLOW, data->remote_low);
+
+		}
+	}
+}
+
+static void max1619_remote_crit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct max1619_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		max1619_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_max);
+		results[1] = TEMP_FROM_REG(data->remote_hyst);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->remote_max = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client,
+				MAX1619_REG_W_REMOTE_TMAX, data->remote_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->remote_hyst = TEMP_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client,
+				MAX1619_REG_W_REMOTE_THYST, data->remote_hyst);
+		}
+	}
+}
+
+static void max1619_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct max1619_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		max1619_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+
+static int __init sm_max1619_init(void)
+{
+	printk(KERN_INFO "max1619.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&max1619_driver);
+}
+
+static void __exit sm_max1619_exit(void)
+{
+	i2c_del_driver(&max1619_driver);
+}
+
+MODULE_AUTHOR("Alexey Fisher <fishor@mail.ru>");
+MODULE_DESCRIPTION("MAX1619 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_max1619_init);
+module_exit(sm_max1619_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/smbus-arp.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/smbus-arp.c	(revision 3176)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/smbus-arp.c	(revision 3176)
@@ -0,0 +1,478 @@
+/*
+    smbus-arp.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2002  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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+#define DEBUG 1
+
+/* So that the module will still compile without i2c-2.6.4;
+   if inserted, it will just put out a message and exit in that case */
+#ifdef I2C_CLIENT_PEC
+#define I2C_PEC_SUPPORTED
+#else
+#define I2C_DRIVERID_ARP        902    /* SMBus ARP Client              */
+#endif
+
+/* Addresses to scan */
+#define	ARP_ADDRESS	0x61
+static unsigned short normal_i2c[] = { ARP_ADDRESS, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(arp);
+
+static int reset;
+MODULE_PARM(reset, "i");
+MODULE_PARM_DESC(reset,
+		 "Send an ARP Reset Device message to each bus");
+
+/* ARP Commands */
+#define	ARP_PREPARE	0x01
+#define	ARP_RESET_DEV	0x02
+#define	ARP_GET_UDID_GEN 0x03
+#define	ARP_ASSIGN_ADDR	0x04
+
+/* UDID Fields */
+#define ARP_CAPAB	0
+#define ARP_VER		1
+#define ARP_VEND	2
+#define ARP_DEV		4
+#define ARP_INT		6
+#define ARP_SUBVEND	8
+#define ARP_SUBDEV	10
+#define ARP_SPECID	12
+
+#define UDID_LENGTH	0x11
+
+static u8 reserved[] =
+/* As defined by SMBus Spec. Appendix C */
+			{0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x28,
+                        0x37, ARP_ADDRESS,
+/* As defined by SMBus Spec. Sect. 5.2 */
+			0x01, 0x02, 0x03, 0x04, 0x05,
+			0x06, 0x07, 0x78, 0x79, 0x7a, 0x7b,
+			0x7c, 0x7d, 0x7e, 0x7f,
+/* Common PC addresses (bad idea) */
+			0x2d, 0x48, 0x49, /* sensors */
+			0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* eeproms */
+			0x69, /* clock chips */
+/* Must end in 0 which is also reserved */
+			0x00};
+
+#define SMBUS_ADDRESS_SIZE	0x80
+#define ARP_FREE	0	
+#define ARP_RESERVED	1
+#define ARP_BUSY	2
+
+#define ARP_MAX_DEVICES 8
+struct arp_device {
+	int status;
+	u8 udid[UDID_LENGTH];
+	u8 dev_cap;
+	u8 dev_ver;
+	u16 dev_vid;
+	u16 dev_did;
+	u16 dev_int;
+	u16 dev_svid;
+	u16 dev_sdid;
+	u32 dev_vsid;
+	u8 saddr;
+};
+
+/* Each client has this additional data */
+struct arp_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 address_pool[SMBUS_ADDRESS_SIZE];
+	struct arp_device dev[ARP_MAX_DEVICES];
+};
+
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter);
+static int smbusarp_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int smbusarp_detach_client(struct i2c_client *client);
+
+static int smbusarp_init_client(struct i2c_client *client);
+static void smbusarp_contents(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver smbusarp_driver = {
+	.name		= "SMBUS ARP",
+	.id		= I2C_DRIVERID_ARP,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= smbusarp_attach_adapter,
+	.detach_client	= smbusarp_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define ARP_SYSCTL1 1000
+#define ARP_SYSCTL2 1001
+#define ARP_SYSCTL3 1002
+#define ARP_SYSCTL4 1003
+#define ARP_SYSCTL5 1004
+#define ARP_SYSCTL6 1005
+#define ARP_SYSCTL7 1006
+#define ARP_SYSCTL8 1007
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table smbusarp_dir_table_template[] = {
+	{ARP_SYSCTL1, "0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL2, "1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL3, "2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL4, "3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL5, "4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL6, "5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL7, "6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{ARP_SYSCTL8, "7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smbusarp_contents},
+	{0}
+};
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, smbusarp_detect);
+}
+
+/* This function is called by i2c_detect */
+int smbusarp_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct arp_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* must support block data w/ HW PEC or SW PEC */
+	if ((!i2c_check_functionality(adapter,
+	        I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_HWPEC_CALC)) &&
+	    (!i2c_check_functionality(adapter,
+	        I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C)))
+		return(0);
+
+	if (!(data = kmalloc(sizeof(struct arp_data), GFP_KERNEL))) {
+		return(-ENOMEM);
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &smbusarp_driver;
+#ifdef I2C_PEC_SUPPORTED
+	new_client->flags = I2C_CLIENT_PEC;
+#else
+	new_client->flags = 0;
+#endif
+
+	type_name = "arp";
+	client_name = "SMBUS ARP client";
+
+	strcpy(new_client->name, client_name);
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	if ((i = i2c_register_entry(new_client, type_name,
+					smbusarp_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+	smbusarp_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR1:
+	kfree(data);
+	return err;
+}
+
+static int smbusarp_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct arp_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    (KERN_ERR "smbus-arp.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+static u8 choose_addr(u8 * pool)
+{
+	int i;
+
+	for(i = 0; i < 0x7f; i++) {
+		if(pool[i] == ARP_FREE)
+			return ((u8) i);
+	}
+	return 0xff;
+}
+
+static int smbusarp_init_client(struct i2c_client *client)
+{
+	int ret = -1;
+#ifdef I2C_PEC_SUPPORTED
+	struct arp_data *data = client->data;
+	u8 blk[I2C_SMBUS_BLOCK_MAX];
+	u8 *r;
+	u8 addr;
+	int i;
+	int found = 0;
+	int newdev = 0;
+	
+	for(i = 0; i < ARP_MAX_DEVICES; i++)
+		data->dev[i].status = ARP_FREE;
+
+	for(i = 0; i < SMBUS_ADDRESS_SIZE; i++)
+		data->address_pool[i] = ARP_FREE;
+
+	r = reserved;
+	do {
+		data->address_pool[*r] = ARP_RESERVED;
+	} while(*r++);
+
+	for (i = 0; i < I2C_CLIENT_MAX ; i++) 
+		if (client->adapter->clients[i])
+			data->address_pool[client->adapter->clients[i]->addr] =
+			                                   ARP_BUSY;
+
+	if(reset)
+		i2c_smbus_write_byte(client, ARP_RESET_DEV);
+	ret = i2c_smbus_write_byte(client, ARP_PREPARE);
+	if(ret < 0) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "smbus-arp.o: No ARP response on adapter 0x%X\n", client->adapter->id);
+#endif
+		return(-1); /* Packet wasn't acked */
+	}
+	while(1) {
+		ret = i2c_smbus_read_block_data(client, ARP_GET_UDID_GEN, blk);
+		if(ret != UDID_LENGTH) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "smbus-arp.o: No/Bad UDID response %d on adapter 0x%X\n", ret, client->adapter->id);
+#endif
+			if(found)
+				return(found);
+			else
+				return(-1); /* Bad response */
+		}
+#ifdef DEBUG
+		printk(KERN_DEBUG "smbus-arp.o: Good UDID response on adapter 0x%X\n", client->adapter->id);
+		printk(KERN_DEBUG "             Cap. 0x%02x  Rev. 0x%02x  Vend. 0x%02x%02x  Dev. 0x%02x%02x\n", blk[0], blk[1], blk[2], blk[3], blk[4], blk[5]);
+		printk(KERN_DEBUG "             Int. 0x%02x%02x  Subvend. 0x%02x%02x  Subdev. 0x%02x%02x  Spec. 0x%02x%02x%02x%02x\n", blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15]);
+#endif
+/* clean up this... */
+		found++;
+		do{
+			if(data->dev[newdev].status == ARP_FREE)
+				break;
+		} while(++newdev < ARP_MAX_DEVICES);
+		if(newdev == ARP_MAX_DEVICES) {
+			printk(KERN_WARNING "smbus-arp.o: No more slots available\n");
+			return(-1);
+		}
+
+		/* check device slave addr */ 		
+		addr = blk[16];
+		if(addr != 0xFF) {
+			addr >>= 1;
+			if(blk[0] & 0xC0) {
+				if(data->address_pool[addr] == ARP_FREE) {
+#ifdef DEBUG
+					printk(KERN_DEBUG "             Requested free Non-fixed Address 0x%02x\n", addr);
+#endif
+				} else {
+#ifdef DEBUG
+					printk(KERN_DEBUG "             Requested busy Non-fixed Address 0x%02x\n", addr);
+#endif
+					if((addr =
+					    choose_addr(data->address_pool)) ==
+					                       0xff) {
+						printk(KERN_WARNING "smbus-arp.o: Address pool exhausted\n");
+						return(-1);
+					}
+				}
+			} else {
+#ifdef DEBUG
+				printk(KERN_DEBUG "             Fixed Address 0x%02x\n", addr);
+#endif
+			}
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG "             No Address\n");
+#endif
+			if((addr = choose_addr(data->address_pool)) == 0xff) {
+				printk(KERN_WARNING "smbus-arp.o: Address pool exhausted\n");
+				return(-1);
+			}
+
+		}
+		/* store things both ways */
+		for(i = 0; i < UDID_LENGTH; i++)
+			data->dev[newdev].udid[i] = blk[i];
+		data->dev[newdev].saddr = addr;
+		data->dev[newdev].status = ARP_BUSY;
+		data->dev[newdev].dev_cap = blk[0];
+		data->dev[newdev].dev_ver = blk[1];
+		data->dev[newdev].dev_vid = (blk[2] << 8) | blk[3];
+		data->dev[newdev].dev_did = (blk[4] << 8) | blk[5];
+		data->dev[newdev].dev_int = (blk[6] << 8) | blk[7];
+		data->dev[newdev].dev_svid = (blk[8] << 8) | blk[9];
+		data->dev[newdev].dev_sdid = (blk[10] << 8) | blk[11];
+		data->dev[newdev].dev_vsid = (blk[12] << 24) | (blk[13] << 16) |
+		                             (blk[14] << 8) | blk[15] ;
+
+		blk[16] = addr << 1;
+		ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR,
+		                                 UDID_LENGTH, blk);
+		if(ret) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "             Bad response, address 0x%02x not assigned\n", addr);
+#endif
+		} else {
+			data->address_pool[addr] = ARP_BUSY;
+#ifdef DEBUG
+			printk(KERN_DEBUG "             Assigned address 0x%02x\n", addr);
+#endif
+		}
+			/* retry? */
+
+	} /* while 1  */
+#else
+	printk(KERN_WARNING "smbus-arp.o: A client responded to the ARP address but "
+	                    "your kernel does not support SMBus ARP/PEC!\n");
+	ret = -EPERM;
+#endif
+	return(ret);
+}
+
+
+/* reassign address on writex */
+void smbusarp_contents(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	int nr = ctl_name - ARP_SYSCTL1;
+	struct arp_data *data = client->data;
+	int ret;
+	u8 save;
+	u8 a;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if(data->dev[nr].status == ARP_BUSY) {
+			results[0] = data->dev[nr].saddr;
+			results[1] = data->dev[nr].dev_cap;
+			results[2] = data->dev[nr].dev_ver;
+			results[3] = data->dev[nr].dev_vid;
+			results[4] = data->dev[nr].dev_did;
+			results[5] = data->dev[nr].dev_int;
+			results[6] = data->dev[nr].dev_svid;
+			results[7] = data->dev[nr].dev_sdid;
+			results[8] = data->dev[nr].dev_vsid;
+			*nrels_mag = 9;
+		} else {
+			*nrels_mag = 0;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		a = results[0];
+		if(*nrels_mag >= 1 &&
+		   a < SMBUS_ADDRESS_SIZE &&
+		   data->dev[nr].status == ARP_BUSY &&
+		   data->address_pool[a] == ARP_FREE) {
+			save = data->dev[nr].udid[16];
+			data->dev[nr].udid[16] = a << 1;
+			ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR,
+                                 UDID_LENGTH, data->dev[nr].udid);
+			if(ret) {
+				data->dev[nr].udid[16] = save;
+#ifdef DEBUG
+				printk(KERN_DEBUG "smbus-arp Bad response, address 0x%02x not assigned\n", a);
+#endif
+			} else {
+				data->dev[nr].saddr = a;
+				data->address_pool[a] = ARP_BUSY;
+#ifdef DEBUG
+				printk(KERN_DEBUG "smbus-arp Assigned address 0x%02x\n", a);
+#endif
+			}
+		} else {
+			printk(KERN_WARNING "smbus-arp Bad address 0x%02x\n", a);
+		}
+	}
+}
+
+static int __init sm_smbusarp_init(void)
+{
+	printk("smbus-arp.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/* magic force invocation */
+	force_arp[0] = -1;
+	force_arp[1] = ARP_ADDRESS;
+	return i2c_add_driver(&smbusarp_driver);
+}
+
+static void __exit sm_smbusarp_exit(void)
+{
+	i2c_del_driver(&smbusarp_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("SMBUS ARP Driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smbusarp_init);
+module_exit(sm_smbusarp_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscher.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscher.c	(revision 3147)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscher.c	(revision 3147)
@@ -0,0 +1,730 @@
+/*
+  fscher.c - Part of lm_sensors, Linux kernel modules for hardware
+  monitoring
+  Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
+  
+  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.
+*/
+
+/* 
+   fujitsu siemens hermes chip, 
+   module based on fscpos.c 
+   Copyright (C) 2000 Hermann Jung <hej@odn.de>
+   Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+   and Philip Edelbrock <phil@netroedge.com>
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+#ifndef I2C_DRIVERID_FSCHER
+#define I2C_DRIVERID_FSCHER		1046
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(fscher);
+
+/* The FSCHER registers */
+
+/* chip identification */
+#define FSCHER_REG_IDENT_0       0x00
+#define FSCHER_REG_IDENT_1       0x01
+#define FSCHER_REG_IDENT_2       0x02
+#define FSCHER_REG_REVISION      0x03
+
+/* global control and status */
+#define FSCHER_REG_EVENT_STATE   0x04
+#define FSCHER_REG_CONTROL       0x05
+
+/* watchdog */
+#define FSCHER_REG_WDOG_PRESET   0x28
+#define FSCHER_REG_WDOG_STATE    0x23
+#define FSCHER_REG_WDOG_CONTROL  0x21
+
+/* fan 0  */
+#define FSCHER_REG_FAN0_MIN      0x55
+#define FSCHER_REG_FAN0_ACT      0x0e
+#define FSCHER_REG_FAN0_STATE    0x0d
+#define FSCHER_REG_FAN0_RIPPLE   0x0f
+
+/* fan 1  */
+#define FSCHER_REG_FAN1_MIN      0x65
+#define FSCHER_REG_FAN1_ACT      0x6b
+#define FSCHER_REG_FAN1_STATE    0x62
+#define FSCHER_REG_FAN1_RIPPLE   0x6f
+
+/* fan 2  */
+#define FSCHER_REG_FAN2_MIN      0xb5
+#define FSCHER_REG_FAN2_ACT      0xbb
+#define FSCHER_REG_FAN2_STATE    0xb2
+#define FSCHER_REG_FAN2_RIPPLE   0xbf
+
+/* voltage supervision */
+#define FSCHER_REG_VOLT_12       0x45
+#define FSCHER_REG_VOLT_5        0x42
+#define FSCHER_REG_VOLT_BATT     0x48
+
+/* temperatures */
+/* sensor 0 */
+#define FSCHER_REG_TEMP0_ACT     0x64
+#define FSCHER_REG_TEMP0_STATE   0x71
+
+/* sensor 1 */
+#define FSCHER_REG_TEMP1_ACT     0x32
+#define FSCHER_REG_TEMP1_STATE   0x81
+
+/* sensor 2 */
+#define FSCHER_REG_TEMP2_ACT     0x35
+#define FSCHER_REG_TEMP2_STATE   0x91
+
+
+
+/* Initial limits */
+
+/* For each registered FSCHER, we need to keep some data in memory. That
+   data is pointed to by fscher_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new fscher client is
+   allocated. */
+struct fscher_data {
+  struct i2c_client client;
+  int sysctl_id;
+
+  struct semaphore update_lock;
+  char valid;	     /* !=0 if following fields are valid */
+  unsigned long last_updated;	/* In jiffies */
+
+  u8  revision;        /* revision of chip */
+  u8  global_event;    /* global event status */
+  u8  global_control;  /* global control register */
+  u8  watchdog[3];     /* watchdog */
+  u8  volt[3];         /* 12, 5, battery current */ 
+  u8  temp_act[3];     /* temperature */
+  u8  temp_status[3];  /* status of sensor */
+  u8  fan_act[3];      /* fans revolutions per second */
+  u8  fan_status[3];   /* fan status */
+  u8  fan_min[3];      /* fan min value for rps */
+  u8  fan_ripple[3];   /* divider for rps */
+};
+
+
+static int fscher_attach_adapter(struct i2c_adapter *adapter);
+static int fscher_detect(struct i2c_adapter *adapter, int address,
+                         unsigned short flags, int kind);
+static int fscher_detach_client(struct i2c_client *client);
+
+static int fscher_read_value(struct i2c_client *client, u8 register);
+static int fscher_write_value(struct i2c_client *client, u8 register,
+                              u8 value);
+static void fscher_update_client(struct i2c_client *client);
+static void fscher_init_client(struct i2c_client *client);
+
+
+static void fscher_in(struct i2c_client *client, int operation, int ctl_name,
+                      int *nrels_mag, long *results);
+static void fscher_pwm(struct i2c_client *client, int operation,
+                       int ctl_name, int *nrels_mag, long *results);
+static void fscher_pwm_internal(struct i2c_client *client, int operation,
+                                int ctl_name, int *nrels_mag, long *results, 
+                                int nr, int reg_min);
+static void fscher_fan(struct i2c_client *client, int operation,
+                       int ctl_name, int *nrels_mag, long *results);
+static void fscher_fan_internal(struct i2c_client *client, int operation,
+                                int ctl_name, int *nrels_mag, long *results, 
+                                int nr, int reg_state, int res_ripple);
+static void fscher_temp(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscher_volt(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscher_wdog(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver fscher_driver = {
+  .name		        = "FSCHER sensor driver",
+  .id		        = I2C_DRIVERID_FSCHER,
+  .flags		= I2C_DF_NOTIFY,
+  .attach_adapter	= fscher_attach_adapter,
+  .detach_client	= fscher_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define FSCHER_SYSCTL_VOLT0    1000       /* 12 volt supply */
+#define FSCHER_SYSCTL_VOLT1    1001       /* 5 volt supply */
+#define FSCHER_SYSCTL_VOLT2    1002       /* batterie voltage */
+#define FSCHER_SYSCTL_FAN0     1101       /* state, ripple, actual value
+                                             fan 0 */
+#define FSCHER_SYSCTL_FAN1     1102       /* state, ripple, actual value
+                                             fan 1 */
+#define FSCHER_SYSCTL_FAN2     1103       /* state, ripple, actual value
+                                             fan 2 */
+#define FSCHER_SYSCTL_TEMP0    1201       /* state and value of sensor 0,
+                                             cpu die */
+#define FSCHER_SYSCTL_TEMP1    1202       /* state and value of sensor 1,
+                                             motherboard */
+#define FSCHER_SYSCTL_TEMP2    1203       /* state and value of sensor 2,
+                                             chassis */
+#define FSCHER_SYSCTL_PWM0     1301       /* min fan 0 */
+#define FSCHER_SYSCTL_PWM1     1302       /* min fan 1 */
+#define FSCHER_SYSCTL_PWM2     1303       /* min fan 2 */
+#define FSCHER_SYSCTL_REV      2000       /* revision */
+#define FSCHER_SYSCTL_EVENT    2001       /* global event status */
+#define FSCHER_SYSCTL_CONTROL  2002       /* global control byte */
+#define FSCHER_SYSCTL_WDOG     2003       /* watch dog preset, state and
+                                             control */
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected FSCHER. 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 fscher_dir_table_template[] = {
+  {FSCHER_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_in},
+  {FSCHER_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_in},
+  {FSCHER_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_in},
+  {FSCHER_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_temp},
+  {FSCHER_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_temp},
+  {FSCHER_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_temp},
+  {FSCHER_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_volt},
+  {FSCHER_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_volt},
+  {FSCHER_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_volt},
+  {FSCHER_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_fan},
+  {FSCHER_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_fan},
+  {FSCHER_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_fan},
+  {FSCHER_SYSCTL_PWM0, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_pwm},
+  {FSCHER_SYSCTL_PWM1, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_pwm},
+  {FSCHER_SYSCTL_PWM2, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_pwm},
+  {FSCHER_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real,
+   &i2c_sysctl_real, NULL, &fscher_wdog},
+  {0}
+};
+
+static int fscher_attach_adapter(struct i2c_adapter *adapter)
+{
+  return i2c_detect(adapter, &addr_data, fscher_detect);
+}
+
+int fscher_detect(struct i2c_adapter *adapter, int address,
+                  unsigned short flags, int kind)
+{
+  int i;
+  struct i2c_client *new_client;
+  struct fscher_data *data;
+  int err = 0;
+  const char *type_name, *client_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("fscher.o: fscher_detect called for an ISA bus adapter?!?\n");
+    return 0;
+  }
+#endif
+
+  if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+    goto ERROR0;
+
+  /* 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 fscher_{read,write}_value. */
+  if (!(data = kmalloc(sizeof(struct fscher_data), GFP_KERNEL))) {
+    err = -ENOMEM;
+    goto ERROR0;
+  }
+
+  new_client = &data->client;
+  new_client->addr = address;
+  new_client->data = data;
+  new_client->adapter = adapter;
+  new_client->driver = &fscher_driver;
+  new_client->flags = 0;
+
+  /* Do the remaining detection unless force or force_fscher parameter */
+  if (kind < 0) {
+    if (fscher_read_value(new_client, FSCHER_REG_IDENT_0) != 0x48) /* 'H' */
+      goto ERROR1;
+    if (fscher_read_value(new_client, FSCHER_REG_IDENT_1) != 0x45) /* 'E' */
+      goto ERROR1;
+    if (fscher_read_value(new_client, FSCHER_REG_IDENT_2) != 0x52) /* 'R' */
+      goto ERROR1;
+  }
+
+  kind = fscher;
+
+  type_name = "fscher";
+  client_name = "fsc hermes chip";
+
+  /* Fill in the remaining client fields and put it into the
+     global list */
+  strcpy(new_client->name, client_name);
+  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(new_client, type_name,
+                              fscher_dir_table_template,
+			      THIS_MODULE)) < 0) {
+    err = i;
+    goto ERROR4;
+  }
+  data->sysctl_id = i;
+
+  fscher_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:
+  kfree(data);
+ ERROR0:
+  return err;
+}
+
+static int fscher_detach_client(struct i2c_client *client)
+{
+  int err;
+
+  i2c_deregister_entry(((struct fscher_data *) (client->data))->sysctl_id);
+
+  if ((err = i2c_detach_client(client))) {
+    printk("fscher.o: Client deregistration failed, client not detached.\n");
+    return err;
+  }
+
+  kfree(client->data);
+
+  return 0;
+}
+
+static int fscher_read_value(struct i2c_client *client, u8 reg)
+{
+#ifdef DEBUG
+  printk("fscher: read reg 0x%02x\n",reg);
+#endif
+  return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+#ifdef DEBUG
+  printk("fscher: write reg 0x%02x, val 0x%02x\n",reg, value);
+#endif
+  return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCHER. */
+static void fscher_init_client(struct i2c_client *client)
+{
+  struct fscher_data *data = client->data;
+
+  /* read revision from chip */
+  data->revision =  fscher_read_value(client,FSCHER_REG_REVISION);
+}
+
+static void fscher_update_client(struct i2c_client *client)
+{
+  struct fscher_data *data = client->data;
+
+  down(&data->update_lock);
+
+  if ((jiffies - data->last_updated > 2 * HZ) ||
+      (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+    printk("Starting fscher update\n");
+#endif
+    data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT);
+    data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT);
+    data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT);
+    data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE);
+    data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE);
+    data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE);
+
+    data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12);
+    data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5);
+    data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT);
+
+    data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT);
+    data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT);
+    data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT);
+    data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE);
+    data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE);
+    data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE);
+    data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN);
+    data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN);
+    data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN);
+    data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE);
+    data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE);
+    data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE);
+
+    data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET);
+    data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE);
+    data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL);
+
+    data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE);
+
+    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. */
+void fscher_in(struct i2c_client *client, int operation, int ctl_name,
+               int *nrels_mag, long *results)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 0;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    switch(ctl_name) {
+    case FSCHER_SYSCTL_REV:
+      results[0] = data->revision ;
+      break;
+    case FSCHER_SYSCTL_EVENT:
+      /* bits 6, 5 and 2 are reserved => mask with 0x9b */
+      results[0] = data->global_event & 0x9b;
+      break;
+    case FSCHER_SYSCTL_CONTROL:
+      results[0] = data->global_control & 0x01;
+      break;
+    default:
+      printk("fscher: ctl_name %d not supported\n", ctl_name);
+      *nrels_mag = 0;
+      return;
+    }
+    *nrels_mag = 1;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    if((ctl_name == FSCHER_SYSCTL_CONTROL) && (*nrels_mag >= 1)) {
+      data->global_control = (results[0] & 0x01);
+      printk("fscher: writing 0x%02x to global_control\n",
+             data->global_control);
+      fscher_write_value(client,FSCHER_REG_CONTROL,
+                         data->global_control);
+    }
+    else
+      printk("fscher: writing to chip not supported\n");
+  }
+}
+
+
+#define TEMP_FROM_REG(val)    (val-128)
+
+void fscher_temp(struct i2c_client *client, int operation, int ctl_name,
+                 int *nrels_mag, long *results)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 0;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    switch(ctl_name) {
+    case FSCHER_SYSCTL_TEMP0:
+      results[0] = data->temp_status[0] & 0x03;
+      results[1] = TEMP_FROM_REG(data->temp_act[0]);
+      break;
+    case FSCHER_SYSCTL_TEMP1:
+      results[0] = data->temp_status[1] & 0x03;
+      results[1] = TEMP_FROM_REG(data->temp_act[1]);
+      break;
+    case FSCHER_SYSCTL_TEMP2:
+      results[0] = data->temp_status[2] & 0x03;
+      results[1] = TEMP_FROM_REG(data->temp_act[2]);
+      break;
+    default:
+      printk("fscher: ctl_name %d not supported\n", ctl_name);
+      *nrels_mag = 0;
+      return;
+    }
+    *nrels_mag = 2;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    if(*nrels_mag >= 1) {
+      switch(ctl_name) {
+      case FSCHER_SYSCTL_TEMP0:
+        data->temp_status[0] = (data->temp_status[0] & ~0x02)
+          | (results[0] & 0x02);
+        printk("fscher: writing value 0x%02x to temp0_status\n",
+               data->temp_status[0]);
+        fscher_write_value(client, FSCHER_REG_TEMP0_STATE,
+                           data->temp_status[0] & 0x02);
+        break;
+      case FSCHER_SYSCTL_TEMP1:
+        data->temp_status[1] = (data->temp_status[1] & ~0x02)
+          | (results[0] & 0x02);
+        printk("fscher: writing value 0x%02x to temp1_status\n",
+               data->temp_status[1]);
+        fscher_write_value(client, FSCHER_REG_TEMP1_STATE,
+                           data->temp_status[1] & 0x02);
+        break;
+      case FSCHER_SYSCTL_TEMP2:
+        data->temp_status[2] = (data->temp_status[2] & ~0x02)
+          | (results[0] & 0x02);
+        printk("fscher: writing value 0x%02x to temp2_status\n",
+               data->temp_status[2]);
+        fscher_write_value(client, FSCHER_REG_TEMP2_STATE,
+                           data->temp_status[2] & 0x02);
+        break;
+      default:
+        printk("fscher: ctl_name %d not supported\n",ctl_name);
+      }
+    }
+    else
+      printk("fscher: writing to chip not supported\n");
+  }
+}
+
+/*
+ * The final conversion is specified in sensors.conf, as it depends on
+ * mainboard specific values. We export the registers contents as
+ * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much
+ * sense per se, but it minimizes the conversions count and keeps the
+ * values within a usual range.
+ */
+void fscher_volt(struct i2c_client *client, int operation, int ctl_name,
+                 int *nrels_mag, long *results)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 2;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    switch(ctl_name) {
+    case FSCHER_SYSCTL_VOLT0:
+      results[0] = data->volt[0];
+      break;
+    case FSCHER_SYSCTL_VOLT1:
+      results[0] = data->volt[1];
+      break;
+    case FSCHER_SYSCTL_VOLT2:
+      results[0] = data->volt[2];
+      break;
+    default:
+      printk("fscher: ctl_name %d not supported\n", ctl_name);
+      *nrels_mag = 0;
+      return;
+    }
+    *nrels_mag = 1;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    printk("fscher: writing to chip not supported\n");
+  }
+}
+
+void fscher_pwm(struct i2c_client *client, int operation, int ctl_name,
+                int *nrels_mag, long *results)
+{
+
+  switch(ctl_name) {
+  case FSCHER_SYSCTL_PWM0:
+    fscher_pwm_internal(client,operation,ctl_name,nrels_mag,results,
+                        0,FSCHER_REG_FAN0_MIN);
+    break;
+  case FSCHER_SYSCTL_PWM1:
+    fscher_pwm_internal(client,operation,ctl_name,nrels_mag,results,
+                        1,FSCHER_REG_FAN1_MIN);
+    break;
+  case FSCHER_SYSCTL_PWM2:
+    fscher_pwm_internal(client,operation,ctl_name,nrels_mag,results,
+                        2,FSCHER_REG_FAN2_MIN);
+    break;
+  default:
+    printk("fscher: illegal pwm nr %d\n",ctl_name);
+  }
+}
+			
+void fscher_pwm_internal(struct i2c_client *client, int operation,
+                         int ctl_name, int *nrels_mag, long *results,
+                         int nr, int reg_min)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 0;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    results[0] = data->fan_min[nr];
+    *nrels_mag = 1;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    if(*nrels_mag >= 1) {  
+      data->fan_min[nr] = results[0];
+      printk("fscher: writing value 0x%02x to fan%d_min\n",
+             data->fan_min[nr],nr);
+      fscher_write_value(client,reg_min,data->fan_min[nr]);
+    }
+  }
+}
+
+void fscher_fan(struct i2c_client *client, int operation, int ctl_name,
+                int *nrels_mag, long *results)
+{
+  switch(ctl_name) {
+  case FSCHER_SYSCTL_FAN0:
+    fscher_fan_internal(client,operation,ctl_name,nrels_mag,results,
+                        0,FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_RIPPLE);
+    break;
+  case FSCHER_SYSCTL_FAN1:
+    fscher_fan_internal(client,operation,ctl_name,nrels_mag,results,
+                        1,FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_RIPPLE);
+    break;
+  case FSCHER_SYSCTL_FAN2:
+    fscher_fan_internal(client,operation,ctl_name,nrels_mag,results,
+                        2,FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_RIPPLE);
+    break;
+  default:
+    printk("fscher: illegal fan nr %d\n",ctl_name);
+  }
+}
+			
+#define RPM_FROM_REG(val)   (val*60)
+
+void fscher_fan_internal(struct i2c_client *client, int operation,
+                         int ctl_name, int *nrels_mag, long *results,
+                         int nr, int reg_state, int reg_ripple)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 0;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    results[0] = data->fan_status[nr] & 0x04;
+    results[1] = data->fan_ripple[nr] & 0x03;
+    results[2] = RPM_FROM_REG(data->fan_act[nr]);
+    *nrels_mag = 3;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    if(*nrels_mag >= 1) {
+      data->fan_status[nr] = results[0] & 0x04;
+      printk("fscher: writing value 0x%02x to fan%d_status\n",
+             data->fan_status[nr],nr);
+      fscher_write_value(client,reg_state,data->fan_status[nr]);
+    }
+    if(*nrels_mag >= 2) {
+      if((results[1] & 0x03) == 0) {
+        printk("fscher: fan%d ripple 0 not allowed\n",nr);
+        return;
+      }
+      data->fan_ripple[nr] = results[1] & 0x03;
+      printk("fscher: writing value 0x%02x to fan%d_ripple\n",
+             data->fan_ripple[nr],nr);
+      fscher_write_value(client,reg_ripple,data->fan_ripple[nr]);
+    }	
+  }
+}
+
+void fscher_wdog(struct i2c_client *client, int operation, int ctl_name,
+                 int *nrels_mag, long *results)
+{
+  struct fscher_data *data = client->data;
+
+  if (operation == SENSORS_PROC_REAL_INFO)
+    *nrels_mag = 0;
+  else if (operation == SENSORS_PROC_REAL_READ) {
+    fscher_update_client(client);
+    results[0] = data->watchdog[0] ;
+    results[1] = data->watchdog[1] & 0x02;
+    results[2] = data->watchdog[2] & 0xd0;
+    *nrels_mag = 3;
+  } else if (operation == SENSORS_PROC_REAL_WRITE) {
+    if (*nrels_mag >= 1) {
+      data->watchdog[0] = results[0] & 0xff;
+      printk("fscher: writing value 0x%02x to wdog_preset\n",
+             data->watchdog[0]); 
+      fscher_write_value(client,FSCHER_REG_WDOG_PRESET,data->watchdog[0]);
+    } 
+    if (*nrels_mag >= 2) {
+      data->watchdog[1] = results[1] & 0x02;
+      printk("fscher: writing value 0x%02x to wdog_state\n",
+             data->watchdog[1]); 
+      fscher_write_value(client,FSCHER_REG_WDOG_STATE,data->watchdog[1]);
+    }
+    if (*nrels_mag >= 3) {
+      data->watchdog[2] = results[2] & 0xf0;
+      printk("fscher: writing value 0x%02x to wdog_control\n",
+             data->watchdog[2]); 
+      fscher_write_value(client,FSCHER_REG_WDOG_CONTROL,data->watchdog[2]);
+    }
+  }
+}
+
+static int __init sm_fscher_init(void)
+{
+  printk("fscher.o version %s (%s)\n", LM_VERSION, LM_DATE);
+  return i2c_add_driver(&fscher_driver);
+}
+
+static void __exit sm_fscher_exit(void)
+{
+  i2c_del_driver(&fscher_driver);
+}
+
+
+
+MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de> based on work from Hermann"
+              " Jung <hej@odn.de>, Frodo Looijaard <frodol@dds.nl> and"
+              " Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("fujitsu siemens hermes chip driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_fscher_init);
+module_exit(sm_fscher_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/bt869.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/bt869.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/bt869.c	(revision 2867)
@@ -0,0 +1,886 @@
+/*
+    bt869.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (c) 2001, 2002  Stephen Davies  <steve@daviesfam.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.
+*/
+
+
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+
+/* found only at 0x44 or 0x45 */
+static unsigned short normal_i2c_range[] = { 0x44, 0x45, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(bt869);
+
+/* Many bt869 constants specified below */
+
+/* The bt869 registers */
+/* Coming soon: Many, many registers */
+
+/* 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. */
+
+   /*none */
+
+/* Initial values */
+/*none*/
+
+/* Each client has this additional data */
+struct bt869_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 status[3];		/* Register values */
+	u16 res[2];		/* Resolution XxY */
+	u8 ntsc;		/* 1=NTSC, 0=PAL */
+	u8 half;		/* go half res */
+	u8 depth;		/* screen depth */
+	u8 colorbars;		/* turn on/off colorbar calibration screen */
+        u8 svideo;              /* output format: (2=RGB) 1=SVIDEO, 0=Composite */
+};
+
+static int bt869_attach_adapter(struct i2c_adapter *adapter);
+static int bt869_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void bt869_init_client(struct i2c_client *client);
+static int bt869_detach_client(struct i2c_client *client);
+static int bt869_read_value(struct i2c_client *client, u8 reg);
+static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void bt869_write_values(struct i2c_client *client, u16 *values);
+static void bt869_status(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void bt869_ntsc(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void bt869_res(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void bt869_half(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void bt869_colorbars(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void bt869_svideo(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void bt869_depth(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void bt869_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver bt869_driver = {
+	.name		= "BT869 video-output chip driver",
+	.id		= I2C_DRIVERID_BT869,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= bt869_attach_adapter,
+	.detach_client	= bt869_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define BT869_SYSCTL_STATUS 1000
+#define BT869_SYSCTL_NTSC   1001
+#define BT869_SYSCTL_HALF   1002
+#define BT869_SYSCTL_RES    1003
+#define BT869_SYSCTL_COLORBARS    1004
+#define BT869_SYSCTL_DEPTH  1005
+#define BT869_SYSCTL_SVIDEO 1006
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected bt869. 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 bt869_dir_table_template[] = {
+	{BT869_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_status},
+	{BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_ntsc},
+	{BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_res},
+	{BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_half},
+	{BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_colorbars},
+	{BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_depth},
+	{BT869_SYSCTL_SVIDEO, "svideo", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_svideo},
+	{0}
+};
+
+/* ******************
+
+720x576, 27.5MHz, PAL, no overscan compensation.
+
+This mode should be use for digital video, DVD playback etc.
+
+NOTE: This mode for PAL, see 720x480 for an equivalent NTSC mode
+NOTE:    -- Steve Davies <steve@daviesfam.org>
+
+
+Compatible X modeline:
+
+    Mode        "720x576-BT869"
+        DotClock        27.5
+        HTimings        720 744 800 880
+        VTimings        576 581 583 625
+    EndMode
+
+
+625LINE=1							625 line output format
+BST_AMP[7:0]=x57 87						Burst ampl. multiplication factor (PAL std??)
+BY_PLL=0							Use the PLL
+CATTENUATE[2:0]=0						No chroma attenuation
+CCF1B1[7:0]=0							close caption stuff
+CCF1B2[7:0]=0							close caption stuff
+CCF2B1[7:0]=0							close caption stuff
+CCF2B2[7:0]=0							close caption stuff
+CCORING[2:0]=0							Bypass chroma coring
+CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0]		Close-caption clock runin start from hsync
+CC_ADD[11:0]=xD2 210  [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2]		Close-caption DTO increment
+CHECK_STAT=0							Don't check monitor status
+CLPF[1:0]=0						  	Hoz chroma lowpass filter=Bypass
+DACDISA=1							Disable DACA
+DACDISB=0							Don't disable DACB
+DACDISC=0							Don't disable DACC
+DACOFF=0							Don't disable the DACs
+DATDLY = 0							normal
+DATSWP=0							normal
+DCHROMA=0							Don't blank chroma
+DIS_FFILT=1							Disable flickerfilter
+DIS_GMSHC=1							Disable chroma psuedo-gamma removal
+DIS_GMSHY=1							Disable luma pseudo gamma removal
+DIS_GMUSHC=1							Disable chroma anti-pseudo gamma removal
+DIS_GMUSHY=1							Disable luma anti-pseudo gamma removal
+DIS_SCRESET=0							Normal subcarrier phase resets
+DIS_YFLPF=0							Disable Luma initial hoz low pass filter
+DIV2=0								Input pixel rate not divided by 2
+ECBAR=0								No colour bars
+ECCF1=0								Disable closed caption
+ECCF2=0								Disable closed caption
+ECCGATE=0							Normal close caption encoding
+ECLIP=0								0=disable clipping
+EN_ASYNC=0							set to 0 for normal operation
+EN_BLANKO=0							BLANK is an input
+EN_DOT=0							Disables dot clock sync on BLANK pin
+EN_OUT=1							Allows outputs to be enabled
+EN_XCLK=1							Use CLKI pin as clock source
+ESTATUS[1:0]=0							Used to select readback register
+FIELDI=0							Logical 1 on FIELD indicates even field
+F_SELC[2:0]=0							5 line chroma flickerfilter
+F_SELY[2:0]=0							5 line luma flickerfilter
+HBURST_BEGIN[7:0]=x98 152					Chroma burst start point in clocks
+HBURST_END[7:0]=x58 88						Chroma burst end point in clocks - 128
+HSYNCI=0							Active low HSYNC
+HSYNC_WIDTH[7:0]=x80 128					Analogue sync width in clocks
+HSYNOFFSET[9:0]=0  [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0]	hsync in "standard position"
+HSYNWIDTH[5:0]=2						2 pixel hsync width
+H_ACTIVE[9:0]=x2D0 720  [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0]	Active pixels per line
+H_BLANKI[8:0]=x84 132  [H_BLANKI[8]=0; H_BLANKI[7:0]=x84]	End of blanking of input video
+H_BLANKO[9:0]=x120 288  [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x20]	End of blanking from hoz sync leading edge
+H_CLKI[10:0]=x378 888  [H_CLKI[10:8]=3; H_CLKI[7:0]=x78]	Input line length total in clocks
+H_CLKO[11:0]=x6e0 1760  [H_CLKO[11:8]=6; H_CLKO[7:0]=xe0]	Output clocks per line
+H_FRACT[7:0]=0							0 fractional input clocks per line
+IN_MODE[2:0]=0							24Bit RGB muxed
+LUMADLY[1:0]=0							0 pixel delay on Y_DLY luma
+MCB[7:0]=x49 73							Mult factor for CB prior to subcarrier mod.
+MCR[7:0]=x82 130						Mult factor for CR prior to subcarrier mod.
+MODE2X=0							Don't divide clock input by 2
+MSC[31:0]=x2945E0B4 692445365  [MSC[31:24]=x29; MSC[23:16]=x45; MSC[15:8]=xE0; MSC[7:0]=xB4] Subcarrier incr.
+MY[7:0]=x8C 140							Mult factor for Y
+NI_OUT=0							Normal interlaced output
+OUT_MODE[1:0]=0							video0-3 is CVBS, Y, C, Y_DLY
+OUT_MUXA[1:0]=0							Don't care as DACA is disabled
+OUT_MUXB[1:0]=1							Output video[1] (Y) on DACB
+OUT_MUXC[1:0]=2							Output video[2] (C) on DACC
+PAL_MD=1							Video output in PAL mode
+PHASE_OFF[7:0]=0						Subcarrier phase offset
+PLL_FRACT[15:0]=x30 48  [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier
+PLL_INT[5:0]=0x0C 12						Int portion of pll multiplier
+SETUP=0								7.5-IRE setup disabled
+SLAVER=1							
+SRESET=0							Don't do a software reset
+SYNC_AMP[7:0]=xF0 240						Sync amp mult. factor (PAL std???)
+VBLANKDLY=0							Extra line of blanking in 2nd field?
+VSYNCI=0							Active low VSYNC
+VSYNC_DUR=0							2.5line VSYNC duration on output
+VSYNCOFFSET[10:0]=0  [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0]	VSYNC in standard position
+VSYNWIDTH[2:0]=1						1 line of vsync width
+V_ACTIVEI[9:0]=x240 576  [V_ACTIVEI[9:0]=2; V_ACTIVEI[7:0]=x40]	Active input lines
+V_ACTIVEO[8:0]=x122 290  [V_ACTIVE0[8]=1; V_ACTIVEO[7:0]=x22]
+V_BLANKI[7:0]=x2A 42						Input lines from vsync to first active line
+V_BLANKO[7:0]=x16 22
+V_LINESI[9:0]=x271 625  [V_LINESI[9:8]=2; V_LINESI[7:0]=x71]	Number of input lines
+V_SCALE[13:0]=x1000 4096  [V_SCALE[13:8]=x10; V_SCALE[7:0]=0]	Vert scale coefficient="none"?
+YATTENUATE[2:0]=0						no luma attenuation
+YCORING[2:0]=0							Luma-coring bypass
+YLPF[1:0]=0							Luma hoz low pass filter=bypass
+
+***************** */
+
+static u16 registers_720_576[] =
+    {
+      0x6e, 0x00,	/* HSYNOFFSET[7:0]=0 */
+      0x70, 0x02,	/* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */
+      0x72, 0x00,	/* VSYNOFFSET[7:0]=0 */
+      0x74, 0x01,	/* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */
+      0x76, 0xe0,	/* H_CLKO[7:0]=xe0 */
+      0x78, 0xd0,	/* H_ACTIVE[7:0]=xD0 */
+      0x7a, 0x80,	/* HSYNC_WIDTH[7:0]=x80 */
+      0x7c, 0x98,	/* HBURST_BEGIN[7:0]=x98 */
+      0x7e, 0x58,	/* HBURST_END[7:0]=x58 */
+      0x80, 0x20,	/* H_BLANKO[7:0]=x20 */
+      0x82, 0x16,	/* V_BLANKO[7:0]=x16 */
+      0x84, 0x22,	/* V_ACTIVEO[7:0]=x22 */
+      0x86, 0xa6,	/* V_ACTIVE0[8]=1; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */
+      0x88, 0x00,	/* H_FRACT[7:0]=0 */
+      0x8a, 0x78,	/* H_CLKI[7:0]=x78 */
+      0x8c, 0x80,	/* H_BLANKI[7:0]=x84 */
+      0x8e, 0x03,	/* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */
+      0x90, 0x71,	/* V_LINESI[7:0]=x71 */
+      0x92, 0x2a,	/* V_BLANKI[7:0]=x2A */
+      0x94, 0x40,	/* V_ACTIVEI[7:0]=x40 */
+      0x96, 0x0a,	/* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:0]=2; V_LINESI[9:8]=2 */
+      0x98, 0x00,	/* V_SCALE[7:0]=0 */
+      0x9a, 0x50,	/* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */
+      0x9c, 0x30,	/* PLL_FRACT[7:0]=x30 */
+      0x9e, 0x0,	/* PLL_FRACT[15:8]=0x0 */
+      0xa0, 0x8c,	/* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */
+      0xa2, 0x24,	/* ECLIP=0; PAL_MD=1; DIS_SCRESET=0; VSYNC_DUR=0; 625LINE=1; SETUP=0; NI_OUT=0 */
+      0xa4, 0xf0,	/* SYNC_AMP[7:0]=xF0 */
+      0xa6, 0x57,	/* BST_AMP[7:0]=x57 */
+      0xa8, 0x82,	/* MCR[7:0]=x82 */
+      0xaa, 0x49,	/* MCB[7:0]=x49 */
+      0xac, 0x8c,	/* MY[7:0]=x8C */
+      0xae, 0xb4,	/* MSC[7:0]=xb4 */
+      0xb0, 0xe0,	/* MSC[15:8]=xe0 */
+      0xb2, 0x45,	/* MSC[23:16]=x45 */
+      0xb4, 0x29,	/* MSC[31:24]=x29 */
+      0xb6, 0x00,	/* PHASE_OFF[7:0]=0 */
+      //0xba, 0x21,	/* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */
+      0xc4, 0x01,	/* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */
+      0xc6, 0x00,	/* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */
+      0xc8, 0x40,	/* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */
+      0xca, 0xc0,	/* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */
+      0xcc, 0xc0,	/* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */
+      //0xce, 0x24,       /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      //0xce, 0x04,       /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      0xd6, 0x00,	/* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */
+      0, 0
+    };
+
+
+/* ******************
+
+720x480, 27.5MHz, NTSC no overscan compensation.
+
+This mode should be use for digital video, DVD playback etc.
+
+NOTE: This mode for NTSC, see 720x576 for an equivalent PAL mode
+NOTE:    -- Steve Davies <steve@daviesfam.org>
+
+Compatible X modeline:
+
+    Mode        "720x480-BT869"
+        DotClock        27.5
+        HTimings        720 744 800 872
+        VTimings        480 483 485 525
+    EndMode
+
+
+625LINE=0							not 625 line output format
+BST_AMP[7:0]=x74 116						Burst ampl. multiplication factor (NTSC std??)
+BY_PLL=0							Use the PLL
+CATTENUATE[2:0]=0						No chroma attenuation
+CCF1B1[7:0]=0							close caption stuff
+CCF1B2[7:0]=0							close caption stuff
+CCF2B1[7:0]=0							close caption stuff
+CCF2B2[7:0]=0							close caption stuff
+CCORING[2:0]=0							Bypass chroma coring
+CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0]		Close-caption clock runin start from hsync
+CC_ADD[11:0]=xD2 210  [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2]		Close-caption DTO increment
+CHECK_STAT=0							Don't check monitor status
+CLPF[1:0]=0						  	Hoz chroma lowpass filter=Bypass
+DACDISA=1							Disable DACA
+DACDISB=0							Don't disable DACB
+DACDISC=0							Don't disable DACC
+DACOFF=0							Don't disable the DACs
+DATDLY = 0							normal
+DATSWP=0							normal
+DCHROMA=0							Don't blank chroma
+DIS_FFILT=1							Disable flickerfilter
+DIS_GMSHC=1							Disable chroma psuedo-gamma removal
+DIS_GMSHY=1							Disable luma pseudo gamma removal
+DIS_GMUSHC=1							Disable chroma anti-pseudo gamma removal
+DIS_GMUSHY=1							Disable luma anti-pseudo gamma removal
+DIS_SCRESET=0							Normal subcarrier phase resets
+DIS_YFLPF=0							Disable Luma initial hoz low pass filter
+DIV2=0								Input pixel rate not divided by 2
+ECBAR=0								No colour bars
+ECCF1=0								Disable closed caption
+ECCF2=0								Disable closed caption
+ECCGATE=0							Normal close caption encoding
+ECLIP=0								0=disable clipping
+EN_ASYNC=0							set to 0 for normal operation
+EN_BLANKO=0							BLANK is an input
+EN_DOT=0							Disables dot clock sync on BLANK pin
+EN_OUT=1							Allows outputs to be enabled
+EN_XCLK=1							Use CLKI pin as clock source
+ESTATUS[1:0]=0							Used to select readback register
+FIELDI=0							Logical 1 on FIELD indicates even field
+F_SELC[2:0]=0							5 line chroma flickerfilter
+F_SELY[2:0]=0							5 line luma flickerfilter
+HBURST_BEGIN[7:0]=x92 146					Chroma burst start point in clocks
+HBURST_END[7:0]=x57 87						Chroma burst end point in clocks - 128
+HSYNCI=0							Active low HSYNC
+HSYNC_WIDTH[7:0]=x80 128					Analogue sync width in clocks
+HSYNOFFSET[9:0]=0  [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0]	hsync in "standard position"
+HSYNWIDTH[5:0]=2						2 pixel hsync width
+H_ACTIVE[9:0]=x2D0 720  [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0]	Active pixels per line
+H_BLANKI[8:0]=x80 128  [H_BLANKI[8]=0; H_BLANKI[7:0]=x80]	End of blanking of input video
+H_BLANKO[9:0]=x102 258  [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x2]	End of blanking from hoz sync leading edge
+H_CLKI[10:0]=x368 872  [H_CLKI[10:8]=3; H_CLKI[7:0]=x68]	Input line length total in clocks
+H_CLKO[11:0]=x6d0 1744  [H_CLKO[11:8]=6; H_CLKO[7:0]=xD0]	Output clocks per line
+H_FRACT[7:0]=0							0 fractional input clocks per line
+IN_MODE[2:0]=0							24Bit RGB muxed
+LUMADLY[1:0]=0							0 pixel delay on Y_DLY luma
+MCB[7:0]=x43 67							Mult factor for CB prior to subcarrier mod.
+MCR[7:0]=x77 119						Mult factor for CR prior to subcarrier mod.
+MODE2X=0							Don't divide clock input by 2
+MSC[31:0]=x215282E5 559055589  [MSC[31:24]=x21; MSC[23:16]=x52; MSC[15:8]=x82; MSC[7:0]=xE5] Subcarrier incr.
+MY[7:0]=x85 133							Mult factor for Y
+NI_OUT=0							Normal interlaced output
+OUT_MODE[1:0]=0							video0-3 is CVBS, Y, C, Y_DLY
+OUT_MUXA[1:0]=0							Don't care as DACA is disabled
+OUT_MUXB[1:0]=1							Output video[1] (Y) on DACB
+OUT_MUXC[1:0]=2							Output video[2] (C) on DACC
+PAL_MD=0							Video output in PAL mode? No.
+PHASE_OFF[7:0]=0						Subcarrier phase offset
+PLL_FRACT[15:0]=x30 48  [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier
+PLL_INT[5:0]=0x0C 12						Int portion of pll multiplier
+SETUP=1								7.5-IRE enabled for NTSC
+SLAVER=1							
+SRESET=0							Don't do a software reset
+SYNC_AMP[7:0]=xE5 229						Sync amp mult. factor (PAL std???)
+VBLANKDLY=0							Extra line of blanking in 2nd field?
+VSYNCI=0							Active low VSYNC
+VSYNC_DUR=1							2.5line VSYNC duration on output (Yes for NTSC)
+VSYNCOFFSET[10:0]=0  [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0]	VSYNC in standard position
+VSYNWIDTH[2:0]=1						1 line of vsync width
+V_ACTIVEI[9:0]=x1E0 480  [V_ACTIVEI[9:0]=1; V_ACTIVEI[7:0]=xE0]	Active input lines
+V_ACTIVEO[8:0]=xF0 240  [V_ACTIVE0[8]=0; V_ACTIVEO[7:0]=xF0]
+V_BLANKI[7:0]=x2A 42						Input lines from vsync to first active line
+V_BLANKO[7:0]=x16 22
+V_LINESI[9:0]=x20D 525  [V_LINESI[9:8]=2; V_LINESI[7:0]=x0D]	Number of input lines
+V_SCALE[13:0]=x1000 4096  [V_SCALE[13:8]=x10; V_SCALE[7:0]=0]	Vert scale coefficient="none"?
+YATTENUATE[2:0]=0						no luma attenuation
+YCORING[2:0]=0							Luma-coring bypass
+YLPF[1:0]=0							Luma hoz low pass filter=bypass
+
+***************** */
+
+static u16 registers_720_480[] =
+    {
+      0x6e, 0x00,	/* HSYNOFFSET[7:0]=0 */
+      0x70, 0x02,	/* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */
+      0x72, 0x00,	/* VSYNOFFSET[7:0]=0 */
+      0x74, 0x01,	/* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */
+      0x76, 0xD0,	/* H_CLKO[7:0]=xD0 */
+      0x78, 0xD0,	/* H_ACTIVE[7:0]=xD0 */
+      0x7a, 0x80,	/* HSYNC_WIDTH[7:0]=x80 */
+      0x7c, 0x92,	/* HBURST_BEGIN[7:0]=x92 */
+      0x7e, 0x57,	/* HBURST_END[7:0]=x57 */
+      0x80, 0x02,	/* H_BLANKO[7:0]=x2 */
+      0x82, 0x16,	/* V_BLANKO[7:0]=x16 */
+      0x84, 0xF0,	/* V_ACTIVEO[7:0]=xF0 */
+      0x86, 0x26,	/* V_ACTIVE0[8]=0; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */
+      0x88, 0x00,	/* H_FRACT[7:0]=0 */
+      0x8a, 0xD0,	/* H_CLKI[7:0]=xD0 */
+      0x8c, 0x80,	/* H_BLANKI[7:0]=x80 */
+      0x8e, 0x03,	/* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */
+      0x90, 0x0D,	/* V_LINESI[7:0]=x0D */
+      0x92, 0x2A,	/* V_BLANKI[7:0]=x2A */
+      0x94, 0xE0,	/* V_ACTIVEI[7:0]=xE0 */
+      0x96, 0x06,	/* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:8]=1; V_LINESI[9:8]=2 */
+      0x98, 0x00,	/* V_SCALE[7:0]=0 */
+      0x9a, 0x50,	/* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */
+      0x9c, 0x30,	/* PLL_FRACT[7:0]=x30 */
+      0x9e, 0x0,	/* PLL_FRACT[15:8]=0x0 */
+      0xa0, 0x8c,	/* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */
+      0xa2, 0x0A,	/* ECLIP=0; PAL_MD=0; DIS_SCRESET=0; VSYNC_DUR=1; 625LINE=0; SETUP=1; NI_OUT=0 */
+      0xa4, 0xE5,	/* SYNC_AMP[7:0]=xE5 */
+      0xa6, 0x74,	/* BST_AMP[7:0]=x74 */
+      0xa8, 0x77,	/* MCR[7:0]=x77 */
+      0xaa, 0x43,	/* MCB[7:0]=x43 */
+      0xac, 0x85,	/* MY[7:0]=x85 */
+      0xae, 0xE5,	/* MSC[7:0]=xE5 */
+      0xb0, 0x82,	/* MSC[15:8]=x82 */
+      0xb2, 0x52,	/* MSC[23:16]=x52 */
+      0xb4, 0x21,	/* MSC[31:24]=x21 */
+      0xb6, 0x00,	/* PHASE_OFF[7:0]=0 */
+      //0xba, 0x21,	/* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */
+      0xc4, 0x01,	/* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */
+      0xc6, 0x00,	/* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */
+      0xc8, 0x40,	/* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */
+      0xca, 0xc0,	/* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */
+      0xcc, 0xc0,	/* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */
+      //0xce, 0x24,       /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      //0xce, 0x04,       /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      0xd6, 0x00,	/* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */
+      0, 0
+    };
+
+
+static int bt869_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, bt869_detect);
+}
+
+/* This function is called by i2c_detect */
+int bt869_detect(struct i2c_adapter *adapter, int address,
+		 unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct bt869_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+
+	printk("bt869.o:  probing address %d .\n", address);
+	/* 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
+		    ("bt869.o: bt869_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE |
+				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		    goto ERROR0;
+
+	/* 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 bt869_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct bt869_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &bt869_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	i2c_smbus_write_byte_data(new_client, 0xC4, 0);	/* set status bank 0 */
+	cur = i2c_smbus_read_byte(new_client);
+	printk("bt869.o: address 0x%X testing-->0x%X\n", address, cur);
+	if ((cur & 0xE0) != 0x20)
+		goto ERROR1;
+
+	/* Determine the chip type */
+	kind = ((cur & 0x20) >> 5);
+
+	if (kind) {
+		type_name = "bt869";
+		client_name = "bt869 chip";
+		printk("bt869.o: BT869 detected\n");
+	} else {
+		type_name = "bt868";
+		client_name = "bt868 chip";
+		printk("bt869.o: BT868 detected\n");
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					bt869_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	bt869_init_client((struct i2c_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int bt869_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct bt869_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("bt869.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are byte-sized.
+   bt869 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int bt869_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte(client);
+}
+
+/* All registers are byte-sized.
+   bt869 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+#ifdef DEBUG
+        printk("bt869.o: write_value(0x%X, 0x%X)\n", reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void bt869_write_values(struct i2c_client *client, u16 *values)
+{
+  /* writes set of registers from array.  0,0 marks end of table */
+  while (*values) {
+    bt869_write_value(client, values[0], values[1]);
+    values += 2;
+  }
+}
+
+static void bt869_init_client(struct i2c_client *client)
+{
+	struct bt869_data *data = client->data;
+
+	/* Initialize the bt869 chip */
+	bt869_write_value(client, 0x0ba, 0x80);
+	//   bt869_write_value(client,0x0D6, 0x00);
+	/* Be a slave to the clock on the Voodoo3 */
+	bt869_write_value(client, 0xa0, 0x80);
+	bt869_write_value(client, 0xba, 0x20);
+	/* depth =16bpp */
+	bt869_write_value(client, 0x0C6, 0x001);
+	bt869_write_value(client, 0xC4, 1);
+	/* Flicker free enable and config */
+	bt869_write_value(client, 0xC8, 0);
+	data->res[0] = 640;
+	data->res[1] = 480;
+	data->ntsc = 1;
+	data->half = 0;
+	data->colorbars = 0;
+	data->svideo = 0;
+	data->depth = 16;
+
+}
+
+static void bt869_update_client(struct i2c_client *client)
+{
+	struct bt869_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk("Starting bt869 update\n");
+#endif
+		if ((data->res[0] == 800) && (data->res[1] == 600)) {
+			/* 800x600 built-in mode */
+		        bt869_write_value(client, 0xB8,
+					  (2 + (!data->ntsc)));
+			bt869_write_value(client, 0xa0, 0x80 + 0x11);
+			printk("bt869.o: writing into config -->0x%X\n",
+			       (2 + (!data->ntsc)));
+		}
+		else if ((data->res[0] == 720) && (data->res[1] == 576)) {
+		        /* 720x576 no-overscan-compensation mode suitable for PAL DVD playback */
+		        data->ntsc = 0; /* This mode always PAL */
+		        bt869_write_values(client,  registers_720_576);
+		}
+		else if ((data->res[0] == 720) && (data->res[1] == 480)) {
+		        /* 720x480 no-overscan-compensation mode suitable for NTSC DVD playback */
+		        data->ntsc = 1; /* This mode always NTSC */
+		        bt869_write_values(client,  registers_720_480);
+		}
+		else {
+		        /* 640x480 built-in mode */
+		        bt869_write_value(client, 0xB8, (!data->ntsc));
+			bt869_write_value(client, 0xa0, 0x80 + 0x0C);
+			printk("bt869.o: writing into config -->0x%X\n",
+			       (0 + (!data->ntsc)));
+			if ((data->res[0] != 640) || (data->res[1] != 480)) {
+			  printk
+			    ("bt869.o:  Warning: arbitrary resolutions not supported yet.  Using 640x480.\n");
+			  data->res[0] = 640;
+			  data->res[1] = 480;
+			}
+		}
+		/* Set colour depth */
+		if ((data->depth != 24) && (data->depth != 16))
+			data->depth = 16;
+		if (data->depth == 16)
+			bt869_write_value(client, 0x0C6, 0x001);
+		if (data->depth == 24)
+			bt869_write_value(client, 0x0C6, 0x000);
+		/* set "half" resolution mode */
+		bt869_write_value(client, 0xd4, data->half << 6);
+		/* Set composite/svideo mode, also enable the right dacs */
+		switch (data->svideo) {
+		case 2:  /* RGB */
+		  /* requires hardware mod on Voodoo3 to get all outputs,
+		     untested in practice... Feedback to steve@daviesfam.org please */
+		  bt869_write_value(client, 0xd6, 0x0c);
+		  bt869_write_value(client, 0xce, 0x24);
+		  bt869_write_value(client, 0xba, 0x20);
+		  break;
+		case 1:  /* Svideo*/
+		  bt869_write_value(client, 0xce, 0x24);
+		  bt869_write_value(client, 0xba, 0x21);
+		  break;
+		default:  /* Composite */
+		  bt869_write_value(client, 0xce, 0x0);
+		  bt869_write_value(client, 0xba, 0x21);
+		  break;
+		}
+		/* Enable outputs */
+		bt869_write_value(client, 0xC4, 1);
+		/* Issue timing reset */
+		bt869_write_value(client, 0x6c, 0x80);
+
+/* Read back status registers */
+		bt869_write_value(client, 0xC4,
+				  1 | (data->colorbars << 2));
+		data->status[0] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x41 | (data->colorbars << 2));
+		data->status[1] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x81 | (data->colorbars << 2));
+		data->status[2] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x0C1 | (data->colorbars << 2));
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+	up(&data->update_lock);
+}
+
+
+void bt869_status(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->status[0];
+		results[1] = data->status[1];
+		results[2] = data->status[2];
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk
+		    ("bt869.o: Warning: write was requested on read-only proc file: status\n");
+	}
+}
+
+
+void bt869_ntsc(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->ntsc;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->ntsc = (results[0] > 0);
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_svideo(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->svideo;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->svideo = results[0];
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_res(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->res[0];
+		results[1] = data->res[1];
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->res[0] = results[0];
+		}
+		if (*nrels_mag >= 2) {
+			data->res[1] = results[1];
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_half(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->half;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->half = (results[0] > 0);
+			bt869_update_client(client);
+		}
+	}
+}
+
+void bt869_colorbars(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->colorbars;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->colorbars = (results[0] > 0);
+			bt869_update_client(client);
+		}
+	}
+}
+
+void bt869_depth(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->depth;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->depth = results[0];
+			bt869_update_client(client);
+		}
+	}
+}
+
+static int __init sm_bt869_init(void)
+{
+	printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&bt869_driver);
+}
+
+static void __exit sm_bt869_exit(void)
+{
+	i2c_del_driver(&bt869_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Stephen Davies <steve@daviesfam.org>");
+MODULE_DESCRIPTION("bt869 driver");
+
+module_init(sm_bt869_init);
+module_exit(sm_bt869_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/saa1064.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/saa1064.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/saa1064.c	(revision 2867)
@@ -0,0 +1,427 @@
+/*
+    saa1064.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2003  Sascha Volkenandt <sascha@akv-soft.de>
+
+    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.
+*/
+
+/* A few notes about the SAA1064:
+
+* The SAA1064 is a driver for 4-digit led displays produced by Philips
+  Semiconductors. It can be found in HiFi equipment with such displays.
+  I've found it inside a Kathrein analogue sat-receiver :-).
+
+* The SAA1064 is quite simple to handle, it receives an address byte,
+  telling which register is addressed, followed by up to five data
+  bytes (according to the data sheet), while with each byte the register 
+  address is increased by one, wrapping from 7 to 0.
+
+* NOTE: This device doesn NOT have eight read-write registers, but eight
+  write-only registers, and one read-only status byte (with no register 
+  address).
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x38, 0x3b, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(saa1064);
+
+/* The SAA1064 registers
+  Registers:
+  0 Control register
+  1 Digit 1
+  2 Digit 2
+  3 Digit 3
+  4 Digit 4
+*/
+
+#define SAA1064_REG_CONTROL 0x00
+#define SAA1064_REG_DIGIT0 0x01
+#define SAA1064_REG_DIGIT1 0x02
+#define SAA1064_REG_DIGIT2 0x03
+#define SAA1064_REG_DIGIT3 0x04
+
+/* Control byte:
+   Bit  0  Dynamic mode 
+        1  Blank digits 1+3 (reversed)
+        2  Blank digits 2+4 (reversed)
+        3  Testmode (all digits on)
+        4  Add 3mA output current to led's
+        5  Add 6mA output current to led's
+        6  Add 12mA output current to led's
+        7  Unused, set to 0
+*/
+
+#define SAA1064_CTRL_DYNAMIC 0x01
+#define SAA1064_CTRL_BLANK13 0x02
+#define SAA1064_CTRL_BLANK24 0x04
+#define SAA1064_CTRL_TEST    0x08
+#define SAA1064_CTRL_BRIGHT  0x70
+
+/* Status byte:
+   The MSB set to 1 indicates powerloss since last read-out */
+#define SAA1064_STAT_PWRLOSS 0x80
+
+/* Get a bit from the control-byte. Except for BRIGHT, these return 
+   zero/non-zero in a boolean fashion. BRIGHT returns a 3-bit value
+   specifying the brightness */
+#define SAA1064_CTRL_GET_DYNAMIC(c) (((c) & SAA1064_CTRL_DYNAMIC)>0)
+#define SAA1064_CTRL_GET_BLANK13(c) (((c) & SAA1064_CTRL_BLANK13)>0)
+#define SAA1064_CTRL_GET_BLANK24(c) (((c) & SAA1064_CTRL_BLANK24)>0)
+#define SAA1064_CTRL_GET_TEST(c)    (((c) & SAA1064_CTRL_TEST)>0)
+#define SAA1064_CTRL_GET_BRIGHT(c)  (((c) & SAA1064_CTRL_BRIGHT)>>4)
+
+/* Get THE bit from the status byte. The LSB set to 1 indicates powerloss
+   since last read-out */
+#define SAA1064_STAT_GET_PWRLOSS(s)  (((s) & SAA1064_STAT_PWRLOSS)>0)
+
+/* Set a bit in the control-byte to val and return the modified byte. 
+   Except for BRIGHT, val is evaluated in a boolean fashion. */
+#define SAA1064_CTRL_SET_DYNAMIC(c,val) ((val)>0 \
+	? (c) | SAA1064_CTRL_DYNAMIC : (c) & ~SAA1064_CTRL_DYNAMIC)
+#define SAA1064_CTRL_SET_BLANK13(c,val) ((val)>0 \
+	? (c) | SAA1064_CTRL_BLANK13 : (c) & ~SAA1064_CTRL_BLANK13)
+#define SAA1064_CTRL_SET_BLANK24(c,val) ((val)>0 \
+	? (c) | SAA1064_CTRL_BLANK24 : (c) & ~SAA1064_CTRL_BLANK24)
+#define SAA1064_CTRL_SET_TEST(c,val) ((val)>0 \
+	? (c) | SAA1064_CTRL_TEST : (c) & ~SAA1064_CTRL_TEST)
+#define SAA1064_CTRL_SET_BRIGHT(c,val) (((c) & ~SAA1064_CTRL_BRIGHT) \
+	| (((val) << 4) & SAA1064_CTRL_BRIGHT))
+
+/* Initial values */
+#define SAA1064_INIT 0x7f	/* All digits on (testmode), full brightness */
+
+/* Each client has this additional data */
+struct saa1064_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;   /* !=0 if following fields are valid */
+	unsigned long last_updated; /* In jiffies */
+
+	u8 control;		/* Control reg */
+	u8 digits[4];	/* Digits regs */
+	u8 status;    /* Status byte (read) */
+};
+
+static int saa1064_attach_adapter(struct i2c_adapter *adapter);
+static int saa1064_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int saa1064_detach_client(struct i2c_client *client);
+
+static void saa1064_bright(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void saa1064_test(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void saa1064_disp(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void saa1064_refresh(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void saa1064_update_client(struct i2c_client *client);
+static void saa1064_init_client(struct i2c_client *client);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver saa1064_driver = {
+	.name           = "SAA1064 sensor chip driver",
+	.id             = I2C_DRIVERID_SAA1064,
+	.flags          = I2C_DF_NOTIFY,
+	.attach_adapter = saa1064_attach_adapter,
+	.detach_client  = saa1064_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define SAA1064_SYSCTL_BRIGHT     1000 /* Brightness, 0-7 */
+#define SAA1064_SYSCTL_TEST       1001 /* Testmode (on = all digits lit) */
+#define SAA1064_SYSCTL_DISP       1005 /* four eight bit values */
+#define SAA1064_SYSCTL_REFRESH    1006 /* refresh digits in case of powerloss */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected SAA1064. 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 saa1064_dir_table_template[] = {
+	{SAA1064_SYSCTL_BRIGHT, "bright", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &saa1064_bright},
+	{SAA1064_SYSCTL_TEST, "test", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &saa1064_test},
+	{SAA1064_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &saa1064_disp},
+	{SAA1064_SYSCTL_REFRESH, "refresh", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &saa1064_refresh},
+	{0}
+};
+
+static int saa1064_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, saa1064_detect);
+}
+
+/* This function is called by i2c_detect */
+static int saa1064_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct saa1064_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("saa1064.o: saa1064_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE 
+			| I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
+		goto ERROR0;
+
+	/* 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 i2c_smbus_read_byte */
+	if (!(data = kmalloc(sizeof(struct saa1064_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &saa1064_driver;
+	new_client->flags = 0;
+
+	if (kind < 0) {
+		if ((i2c_smbus_read_byte(new_client) & ~SAA1064_STAT_PWRLOSS) != 0x00
+				|| i2c_smbus_read_byte(new_client) != 0x00
+				|| i2c_smbus_read_byte(new_client) != 0x00
+				|| i2c_smbus_read_byte(new_client) != 0x00)
+			goto ERROR1;
+	}
+
+	/* If detection was requested, it has also passed til now. Only one chip, 
+	   so no further evaluation of kind. */
+	kind = saa1064;
+	type_name = "saa1064";
+	client_name = "SAA1064 chip";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					saa1064_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+#ifdef DEBUG
+	printk("saa1064.o: Module initialization complete.\n");
+#endif
+
+	/* Initialize the SAA1064 chip */
+	saa1064_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+
+int saa1064_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct saa1064_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("saa1064.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+/* Called when we have found a new SAA1064. */
+void saa1064_init_client(struct i2c_client *client)
+{
+	struct saa1064_data *data = client->data;
+	data->control = SAA1064_INIT;
+	memset(data->digits, 0x00, 4);
+
+	i2c_smbus_write_byte_data(client, SAA1064_REG_CONTROL, data->control);
+	i2c_smbus_write_i2c_block_data(client, SAA1064_REG_DIGIT0, 4, data->digits);
+}
+
+/* Update status byte, and write registers in case a powerloss occurred */
+static void saa1064_update_client(struct i2c_client *client)
+{
+	struct saa1064_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 5*HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("saa1064.o: starting saa1064 update\n");
+#endif
+
+		data->status = i2c_smbus_read_byte(client);
+		if (SAA1064_STAT_GET_PWRLOSS(data->status)) {
+			i2c_smbus_write_byte_data(client, SAA1064_REG_CONTROL, data->control);
+			i2c_smbus_write_i2c_block_data(client, SAA1064_REG_DIGIT0, 4, data->digits);
+		}
+		
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+static void saa1064_bright(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct saa1064_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = SAA1064_CTRL_GET_BRIGHT(data->control);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->control = SAA1064_CTRL_SET_BRIGHT(data->control, results[0]);
+			i2c_smbus_write_byte_data(client, SAA1064_REG_CONTROL, data->control);
+		}
+	}
+}
+
+
+static void saa1064_test(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct saa1064_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = SAA1064_CTRL_GET_TEST(data->control);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->control = SAA1064_CTRL_SET_TEST(data->control, results[0]);
+			i2c_smbus_write_byte_data(client, SAA1064_REG_CONTROL, data->control);
+		}
+	}
+}
+
+
+static void saa1064_disp(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct saa1064_data *data = client->data;
+	int i;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		for (i = 0; i < 4; ++i)
+			results[i] = (long)data->digits[i];
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i = 0; i < max(*nrels_mag, 4); ++i)
+			data->digits[i] = (u8)results[i];
+		i2c_smbus_write_i2c_block_data(client, SAA1064_REG_DIGIT0, 4, data->digits);
+	}
+}
+
+
+static void saa1064_refresh(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		saa1064_update_client(client);
+		*nrels_mag = 0;
+	}  
+}
+
+
+static int __init sensors_saa1064_init(void)
+{
+	printk("saa1064.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&saa1064_driver);
+}
+
+
+static void __exit sensors_saa1064_exit(void)
+{
+	i2c_del_driver(&saa1064_driver);
+}
+
+
+
+MODULE_AUTHOR("Sascha Volkenandt <sascha@akv-soft.de>");
+MODULE_DESCRIPTION("SAA1064 driver");
+
+module_init(sensors_saa1064_init);
+module_exit(sensors_saa1064_exit);
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm80.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm80.c	(revision 3156)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm80.c	(revision 3156)
@@ -0,0 +1,599 @@
+/*
+    lm80.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* 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[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm80);
+
+/* Many LM80 constants specified below */
+
+/* The LM80 registers */
+#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
+#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
+#define LM80_REG_IN(nr) (0x20 + (nr))
+
+#define LM80_REG_FAN1_MIN 0x3c
+#define LM80_REG_FAN2_MIN 0x3d
+#define LM80_REG_FAN1 0x28
+#define LM80_REG_FAN2 0x29
+
+#define LM80_REG_TEMP 0x27
+#define LM80_REG_TEMP_HOT_MAX 0x38
+#define LM80_REG_TEMP_HOT_HYST 0x39
+#define LM80_REG_TEMP_OS_MAX 0x3a
+#define LM80_REG_TEMP_OS_HYST 0x3b
+
+#define LM80_REG_CONFIG 0x00
+#define LM80_REG_ALARM1 0x01
+#define LM80_REG_ALARM2 0x02
+#define LM80_REG_MASK1 0x03
+#define LM80_REG_MASK2 0x04
+#define LM80_REG_FANDIV 0x05
+#define LM80_REG_RES 0x06
+
+
+/* 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),0,255))
+#define IN_FROM_REG(val) (val)
+
+static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned 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/((div)*(val)))
+
+static inline long TEMP_FROM_REG(u16 temp)
+{
+	long res;
+
+	temp >>= 4;
+	if (temp < 0x0800)
+		res = 625 * (long) temp;
+	else
+		res = ((long) temp - 0x01000) * 625;
+
+	return res / 100;
+}
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-50)/100):\
+                                                      ((val)+50)/100), \
+                                             0,255)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+struct lm80_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 temp;		/* Register values, shifted right */
+	u8 temp_hot_max;	/* Register value */
+	u8 temp_hot_hyst;	/* Register value */
+	u8 temp_os_max;		/* Register value */
+	u8 temp_os_hyst;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+
+static int lm80_attach_adapter(struct i2c_adapter *adapter);
+static int lm80_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int lm80_detach_client(struct i2c_client *client);
+
+static int lm80_read_value(struct i2c_client *client, u8 reg);
+static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
+static void lm80_update_client(struct i2c_client *client);
+static void lm80_init_client(struct i2c_client *client);
+
+
+static void lm80_in(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results);
+static void lm80_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm80_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm80_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm80_fan_div(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver lm80_driver = {
+	.name		= "LM80 sensor driver",
+	.id		= I2C_DRIVERID_LM80,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm80_attach_adapter,
+	.detach_client	= lm80_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM80_SYSCTL_IN0 1000	/* Volts * 100 */
+#define LM80_SYSCTL_IN1 1001
+#define LM80_SYSCTL_IN2 1002
+#define LM80_SYSCTL_IN3 1003
+#define LM80_SYSCTL_IN4 1004
+#define LM80_SYSCTL_IN5 1005
+#define LM80_SYSCTL_IN6 1006
+#define LM80_SYSCTL_FAN1 1101	/* Rotations/min */
+#define LM80_SYSCTL_FAN2 1102
+#define LM80_SYSCTL_TEMP 1250	/* Degrees Celsius * 100 */
+#define LM80_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define LM80_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define LM80_ALARM_IN0 0x0001
+#define LM80_ALARM_IN1 0x0002
+#define LM80_ALARM_IN2 0x0004
+#define LM80_ALARM_IN3 0x0008
+#define LM80_ALARM_IN4 0x0010
+#define LM80_ALARM_IN5 0x0020
+#define LM80_ALARM_IN6 0x0040
+#define LM80_ALARM_FAN1 0x0400
+#define LM80_ALARM_FAN2 0x0800
+#define LM80_ALARM_TEMP_HOT 0x0100
+#define LM80_ALARM_TEMP_OS 0x2000
+#define LM80_ALARM_CHAS 0x1000
+#define LM80_ALARM_BTI 0x0200
+#define LM80_ALARM_INT_IN 0x0080
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected LM80. 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 lm80_dir_table_template[] = {
+	{LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_temp},
+	{LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan_div},
+	{LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_alarms},
+	{0}
+};
+
+static int lm80_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm80_detect);
+}
+
+static int lm80_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct lm80_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("lm80.o: lm80_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* 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 lm80_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm80_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0)
+		goto ERROR1;
+	for (i = 0x2a; i <= 0x3d; i++) {
+		cur = i2c_smbus_read_byte_data(new_client, i);
+		if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur)
+		    || (i2c_smbus_read_byte_data(new_client, i + 0x80) !=
+			cur)
+		    || (i2c_smbus_read_byte_data(new_client, i + 0xc0) !=
+			cur)) goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = lm80;
+
+	if (kind == lm80) {
+		type_name = "lm80";
+		client_name = "LM80 chip";
+	} else {
+#ifdef DEBUG
+		printk("lm80.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					lm80_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	lm80_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int lm80_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm80_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm80.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int lm80_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM80. */
+static void lm80_init_client(struct i2c_client *client)
+{
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others. This makes most other
+	   initializations unnecessary */
+	lm80_write_value(client, LM80_REG_CONFIG, 0x80);
+	/* Set 11-bit temperature resolution */
+	lm80_write_value(client, LM80_REG_RES, 0x08);
+
+	/* Start monitoring */
+	lm80_write_value(client, LM80_REG_CONFIG, 0x01);
+}
+
+static void lm80_update_client(struct i2c_client *client)
+{
+	struct lm80_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting lm80 update\n");
+#endif
+		for (i = 0; i <= 6; i++) {
+			data->in[i] =
+			    lm80_read_value(client, LM80_REG_IN(i));
+			data->in_min[i] =
+			    lm80_read_value(client, LM80_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm80_read_value(client, LM80_REG_IN_MAX(i));
+		}
+		data->fan[0] = lm80_read_value(client, LM80_REG_FAN1);
+		data->fan_min[0] =
+		    lm80_read_value(client, LM80_REG_FAN1_MIN);
+		data->fan[1] = lm80_read_value(client, LM80_REG_FAN2);
+		data->fan_min[1] =
+		    lm80_read_value(client, LM80_REG_FAN2_MIN);
+
+		data->temp =
+		    (lm80_read_value(client, LM80_REG_TEMP) << 8) |
+		    (lm80_read_value(client, LM80_REG_RES) & 0xf0);
+		data->temp_os_max =
+		    lm80_read_value(client, LM80_REG_TEMP_OS_MAX);
+		data->temp_os_hyst =
+		    lm80_read_value(client, LM80_REG_TEMP_OS_HYST);
+		data->temp_hot_max =
+		    lm80_read_value(client, LM80_REG_TEMP_HOT_MAX);
+		data->temp_hot_hyst =
+		    lm80_read_value(client, LM80_REG_TEMP_HOT_HYST);
+
+		i = lm80_read_value(client, LM80_REG_FANDIV);
+		data->fan_div[0] = (i >> 2) & 0x03;
+		data->fan_div[1] = (i >> 4) & 0x03;
+		data->alarms = lm80_read_value(client, LM80_REG_ALARM1) +
+		    (lm80_read_value(client, LM80_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. */
+void lm80_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int nr = ctl_name - LM80_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			lm80_write_value(client, LM80_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			lm80_write_value(client, LM80_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+	}
+}
+
+void lm80_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int nr = ctl_name - LM80_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_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]));
+			lm80_write_value(client,
+					 nr ==
+					 1 ? LM80_REG_FAN1_MIN :
+					 LM80_REG_FAN2_MIN,
+					 data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void lm80_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst);
+		results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
+		results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
+		results[4] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm80_write_value(client, LM80_REG_TEMP_HOT_MAX,
+					 data->temp_hot_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hot_hyst =
+			    TEMP_LIMIT_TO_REG(results[1]);
+			lm80_write_value(client, LM80_REG_TEMP_HOT_HYST,
+					 data->temp_hot_hyst);
+		}
+		if (*nrels_mag >= 3) {
+			data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]);
+			lm80_write_value(client, LM80_REG_TEMP_OS_MAX,
+					 data->temp_os_max);
+		}
+		if (*nrels_mag >= 4) {
+			data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]);
+			lm80_write_value(client, LM80_REG_TEMP_OS_HYST,
+					 data->temp_os_hyst);
+		}
+	}
+}
+
+void lm80_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = 2;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = lm80_read_value(client, LM80_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0xcf) | (data->fan_div[1] << 4);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xf3) | (data->fan_div[0] << 2);
+			lm80_write_value(client, LM80_REG_FANDIV, old);
+		}
+	}
+}
+
+static int __init sm_lm80_init(void)
+{
+ 	printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm80_driver);
+}
+
+static void __exit sm_lm80_exit(void)
+{
+	i2c_del_driver(&lm80_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LM80 driver");
+
+module_init(sm_lm80_init);
+module_exit(sm_lm80_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1621.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1621.c	(revision 3156)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1621.c	(revision 3156)
@@ -0,0 +1,537 @@
+/*
+    ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Christian W. Zuckschwerdt  <zany@triq.net>  2000-11-23
+    based on lm75.c by 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.
+*/
+
+/* Supports DS1621. See doc/chips/ds1621 for details */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(ds1621);
+
+/* Many DS1621 constants specified below */
+
+/* Config register used for detection         */
+/*  7    6    5    4    3    2    1    0      */
+/* |Done|THF |TLF |NVB | X  | X  |POL |1SHOT| */
+#define DS1621_REG_CONFIG_NVB 0x10
+#define DS1621_REG_CONFIG_POLARITY 0x02
+#define DS1621_REG_CONFIG_1SHOT 0x01
+#define DS1621_REG_CONFIG_DONE 0x80
+
+/* Note: the done bit is always unset if continuous conversion is in progress.
+         We need to stop the continuous conversion or switch to single shot
+         before this bit becomes available!
+ */
+
+/* The DS1621 registers */
+#define DS1621_REG_TEMP 0xAA /* word, RO */
+#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */
+#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */
+#define DS1621_REG_CONF 0xAC /* byte, RW */
+#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */
+#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */
+#define DS1621_COM_START 0xEE /* no data */
+#define DS1621_COM_STOP 0x22 /* no data */
+
+/* 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 ALARMS_FROM_REG(val) ((val) & \
+                              (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
+#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \
+                            ((val & 0x8000)?-256:0))
+
+/* Each client has this additional data */
+struct ds1621_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_over, temp_hyst;	/* Register values, word */
+	u8 conf;			/* Register encoding, combined */
+
+	char enable;	/* !=0 if we're expected to restart the conversion */
+	u8 temp_int, temp_counter, temp_slope;	/* Register values, byte */
+};
+
+static int ds1621_attach_adapter(struct i2c_adapter *adapter);
+static int ds1621_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void ds1621_init_client(struct i2c_client *client);
+static int ds1621_detach_client(struct i2c_client *client);
+
+static int ds1621_read_value(struct i2c_client *client, u8 reg);
+static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void ds1621_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void ds1621_alarms(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void ds1621_enable(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void ds1621_continuous(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void ds1621_polarity(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ds1621_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ds1621_driver = {
+	.name		= "DS1621 sensor driver",
+	.id		= I2C_DRIVERID_DS1621,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ds1621_attach_adapter,
+	.detach_client	= ds1621_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define DS1621_SYSCTL_TEMP 1200	/* Degrees Celsius * 10 */
+#define DS1621_SYSCTL_ALARMS 2001	/* bitvector */
+#define DS1621_ALARM_TEMP_HIGH 0x40
+#define DS1621_ALARM_TEMP_LOW 0x20
+#define DS1621_SYSCTL_ENABLE 2002
+#define DS1621_SYSCTL_CONTINUOUS 2003
+#define DS1621_SYSCTL_POLARITY 2004
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected DS1621. 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 ds1621_dir_table_template[] = {
+	{DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp},
+	{DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms},
+	{DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable},
+	{DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous},
+	{DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity},
+	{0}
+};
+
+static int ds1621_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, ds1621_detect);
+}
+
+/* This function is called by i2c_detect */
+static int ds1621_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind)
+{
+	int i, conf, temp;
+	struct i2c_client *new_client;
+	struct ds1621_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		 ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA |
+				     I2C_FUNC_SMBUS_WRITE_BYTE))
+		    goto ERROR0;
+
+	/* 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 ds1621_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ds1621_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (kind < 0) {
+		/* The NVB bit should be low if no EEPROM write has been
+		   requested during the latest 10ms, which is highly
+		   improbable in our case. */
+		conf = i2c_smbus_read_byte_data(new_client,
+						DS1621_REG_CONF);
+		if (conf & DS1621_REG_CONFIG_NVB)
+			goto ERROR1;
+		/* The 7 lowest bits of a temperature should always be 0. */
+		temp = ds1621_read_value(new_client, 
+					 DS1621_REG_TEMP);
+		if (temp & 0x007f)
+			goto ERROR1;
+		temp = ds1621_read_value(new_client, 
+					 DS1621_REG_TEMP_HYST);
+		if (temp & 0x007f)
+			goto ERROR1;
+		temp = ds1621_read_value(new_client, 
+					 DS1621_REG_TEMP_OVER);
+		if (temp & 0x007f)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = ds1621;
+
+	if (kind == ds1621) {
+		type_name = "ds1621";
+		client_name = "DS1621 chip";
+	} else {
+#ifdef DEBUG
+		printk("ds1621.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					ds1621_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	ds1621_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int ds1621_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct ds1621_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+	   ("ds1621.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are word-sized, except for the configuration register.
+   DS1621 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int ds1621_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
+	    || (reg == DS1621_REG_TEMP_SLOPE))
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+/* All registers are word-sized, except for the configuration register.
+   DS1621 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ( (reg == DS1621_COM_START) || (reg == DS1621_COM_STOP) )
+		return i2c_smbus_write_byte(client, reg);
+	else
+	if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
+	    || (reg == DS1621_REG_TEMP_SLOPE))
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void ds1621_init_client(struct i2c_client *client)
+{
+	int reg;
+
+	reg = ds1621_read_value(client, DS1621_REG_CONF);
+	/* start the continuous conversion */
+	if(reg & 0x01)
+		ds1621_write_value(client, DS1621_REG_CONF, reg & 0xfe);
+}
+
+static void ds1621_update_client(struct i2c_client *client)
+{
+	struct ds1621_data *data = client->data;
+	u8 new_conf;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting ds1621 update\n");
+#endif
+
+		data->conf = ds1621_read_value(client, DS1621_REG_CONF);
+
+		data->temp = ds1621_read_value(client,
+					       DS1621_REG_TEMP);
+		data->temp_over = ds1621_read_value(client,
+		                                    DS1621_REG_TEMP_OVER);
+		data->temp_hyst = ds1621_read_value(client,
+						    DS1621_REG_TEMP_HYST);
+
+		/* wait for the DONE bit before reading extended values */
+
+		if (data->conf & DS1621_REG_CONFIG_DONE) {
+			data->temp_counter = ds1621_read_value(client,
+						     DS1621_REG_TEMP_COUNTER);
+			data->temp_slope = ds1621_read_value(client,
+						     DS1621_REG_TEMP_SLOPE);
+			data->temp_int = ITEMP_FROM_REG(data->temp);
+			/* restart the conversion */
+			if (data->enable)
+				ds1621_write_value(client, DS1621_COM_START, 0);
+		}
+
+		/* reset alarms if necessary */
+		new_conf = data->conf;
+		if (data->temp < data->temp_over)
+			new_conf &= ~DS1621_ALARM_TEMP_HIGH;
+		if (data->temp > data->temp_hyst)
+			new_conf &= ~DS1621_ALARM_TEMP_LOW;
+		if (data->conf != new_conf)
+			ds1621_write_value(client, DS1621_REG_CONF,
+					   new_conf);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void ds1621_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		if (!(data->conf & DS1621_REG_CONFIG_DONE) ||
+		    (data->temp_counter > data->temp_slope) ||
+		    (data->temp_slope == 0)) {
+			*nrels_mag = 1;
+		} else {
+			*nrels_mag = 2;
+		}
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		/* decide wether to calculate more precise temp */
+		if (!(data->conf & DS1621_REG_CONFIG_DONE) ||
+		    (data->temp_counter > data->temp_slope) ||
+		    (data->temp_slope == 0)) {
+			results[0] = TEMP_FROM_REG(data->temp_over);
+			results[1] = TEMP_FROM_REG(data->temp_hyst);
+			results[2] = TEMP_FROM_REG(data->temp);
+		} else {
+			results[0] = TEMP_FROM_REG(data->temp_over)*10;
+			results[1] = TEMP_FROM_REG(data->temp_hyst)*10;
+			results[2] = data->temp_int * 100 - 25 +
+				((data->temp_slope - data->temp_counter) *
+				 100 / data->temp_slope);
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			ds1621_write_value(client, DS1621_REG_TEMP_OVER,
+					 data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			ds1621_write_value(client, DS1621_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->conf);
+		*nrels_mag = 1;
+	}
+}
+
+void ds1621_enable(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	/* If you really screw up your chip (like I did) this is */
+	/* sometimes needed to (re)start the continuous conversion */
+	/* there is no data to read so this might hang your SMBus! */
+
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(data->conf & DS1621_REG_CONFIG_DONE);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_COM_START, 0);
+				data->enable=1;
+			} else {
+				ds1621_write_value(client, DS1621_COM_STOP, 0);
+				data->enable=0;
+			}
+		} else {
+			ds1621_write_value(client, DS1621_COM_START, 0);
+			data->enable=1;
+		}
+	}
+}
+
+void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name,
+		       int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		ds1621_update_client(client);
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf & ~DS1621_REG_CONFIG_1SHOT);
+			} else {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf | DS1621_REG_CONFIG_1SHOT);
+			}
+		} else {
+			ds1621_write_value(client, DS1621_REG_CONF,
+					   data->conf & ~DS1621_REG_CONFIG_1SHOT);
+		}
+	}
+}
+
+void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name,
+		     int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY));
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		ds1621_update_client(client);
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf | DS1621_REG_CONFIG_POLARITY);
+			} else {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf & ~DS1621_REG_CONFIG_POLARITY);
+			}
+		}
+	}
+}
+
+static int __init sm_ds1621_init(void)
+{
+	printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ds1621_driver);
+}
+
+static void __exit sm_ds1621_exit(void)
+{
+	i2c_del_driver(&ds1621_driver);
+}
+
+
+
+MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
+MODULE_DESCRIPTION("DS1621 driver");
+
+module_init(sm_ds1621_init);
+module_exit(sm_ds1621_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm63.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm63.c	(revision 2994)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm63.c	(revision 2994)
@@ -0,0 +1,645 @@
+/*
+ * lm63.c - driver for the National Semiconductor LM63 temperature sensor
+ *          with integrated fan control
+ * Copyright (C) 2004  Jean Delvare <khali@linux-fr.org>
+ * Based on the lm90 driver.
+ *
+ * The LM63 is a sensor chip made by National Semiconductor. It measures
+ * two temperatures (its own and one external one) and the speed of one
+ * fan, those speed it can additionally control. Complete datasheet can be
+ * obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM63.html
+ *
+ * The LM63 is basically an LM86 with fan speed monitoring and control
+ * capabilities added. It misses some of the LM86 features though:
+ *  - No low limit for local temperature.
+ *  - No critical limit for local temperature.
+ *  - Critical limit for remote temperature can be changed only once. We
+ *    will consider that the critical limit is read-only.
+ *
+ * The datasheet isn't very clear about what the tachometer reading is.
+ * I had a explanation from National Semiconductor though. The two lower
+ * bits of the read value have to be masked out. The value is still 16 bit
+ * in width.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+/*
+ * Addresses to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static unsigned short normal_i2c[] = { 0x4c, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm63);
+
+/*
+ * The LM63 registers
+ */
+
+#define LM63_REG_CONFIG1		0x03
+#define LM63_REG_CONFIG2		0xBF
+#define LM63_REG_CONFIG_FAN		0x4A
+
+#define LM63_REG_TACH_COUNT_MSB		0x47
+#define LM63_REG_TACH_COUNT_LSB		0x46
+#define LM63_REG_TACH_LIMIT_MSB		0x49
+#define LM63_REG_TACH_LIMIT_LSB		0x48
+
+#define LM63_REG_PWM_VALUE		0x4C
+#define LM63_REG_PWM_FREQ		0x4D
+
+#define LM63_REG_LOCAL_TEMP		0x00
+#define LM63_REG_LOCAL_HIGH		0x05
+
+#define LM63_REG_REMOTE_TEMP_MSB	0x01
+#define LM63_REG_REMOTE_TEMP_LSB	0x10
+#define LM63_REG_REMOTE_OFFSET_MSB	0x11
+#define LM63_REG_REMOTE_OFFSET_LSB	0x12
+#define LM63_REG_REMOTE_HIGH_MSB	0x07
+#define LM63_REG_REMOTE_HIGH_LSB	0x13
+#define LM63_REG_REMOTE_LOW_MSB		0x08
+#define LM63_REG_REMOTE_LOW_LSB		0x14
+#define LM63_REG_REMOTE_TCRIT		0x19
+#define LM63_REG_REMOTE_TCRIT_HYST	0x21
+
+#define LM63_REG_ALERT_STATUS		0x02
+#define LM63_REG_ALERT_MASK		0x16
+
+#define LM63_REG_MAN_ID			0xFE
+#define LM63_REG_CHIP_ID		0xFF
+
+/*
+ * Conversions and various macros
+ * For tachometer counts, the LM63 uses 16-bit values.
+ * For local temperature and high limit, remote critical limit and hysteresis
+ * value, it uses signed 8-bit values with LSB = 1 degree Celsius.
+ * For remote temperature, low and high limits, it uses signed 11-bit values
+ * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
+ */
+
+#define FAN_FROM_REG(reg)	((reg) == 0xFFFC || (reg) == 0 ? 0 : \
+				 5400000 / (reg))
+#define FAN_TO_REG(val)		((val) <= 82 ? 0xFFFF : \
+				 (5400000 / (val)) & 0xFFFC)
+#define TEMP8_FROM_REG(reg)	(reg)
+#define TEMP8_TO_REG(val)	((val) <= -128 ? -128 : \
+				 (val) >= 127 ? 127 : \
+				 val)
+#define TEMP11_FROM_REG(reg)	((reg) / 32 * 125)
+#define TEMP11_TO_REG(val)	((val) <= -128000 ? 0x8000 : \
+				 (val) >= 127875 ? 0x7FE0 : \
+				 (val) < 0 ? ((val) - 62) / 125 * 32 : \
+				 ((val) + 62) / 125 * 32)
+#define HYST_TO_REG(val)	((val) <= 0 ? 0 : \
+				 (val) >= 127 ? 127 : \
+				 val)
+
+/*
+ * Functions declaration
+ */
+
+static int lm63_attach_adapter(struct i2c_adapter *adapter);
+static int lm63_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind);
+static void lm63_init_client(struct i2c_client *client);
+static int lm63_detach_client(struct i2c_client *client);
+
+static void lm63_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_remote_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_remote_tcrit_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_fan(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm63_pwm(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm63_driver = {
+	.name		= "LM63 sensor driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm63_attach_adapter,
+	.detach_client	= lm63_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm63_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 config, config_fan;
+	u16 fan1_input;
+	u16 fan1_low;
+	u8 pwm1_freq;
+	u8 pwm1_value;
+	s8 temp1_input;
+	s8 temp1_high;
+	s16 temp2_input;
+	s16 temp2_high;
+	s16 temp2_low;
+	s8 temp2_crit;
+	u8 temp2_crit_hyst;
+	u8 alarms;
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected LM63.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM63_SYSCTL_TEMP1		1200
+#define LM63_SYSCTL_TEMP2		1201
+#define LM63_SYSCTL_TEMP2_TCRIT		1205
+#define LM63_SYSCTL_TEMP2_TCRIT_HYST	1208
+#define LM63_SYSCTL_ALARMS		1210
+#define LM63_SYSCTL_FAN1		1220
+#define LM63_SYSCTL_PWM1		1230
+
+#define LM63_ALARM_LOCAL_HIGH		0x40
+#define LM63_ALARM_REMOTE_HIGH		0x10
+#define LM63_ALARM_REMOTE_LOW		0x08
+#define LM63_ALARM_REMOTE_CRIT		0x02
+#define LM63_ALARM_REMOTE_OPEN		0x04
+#define LM63_ALARM_FAN_LOW		0x01
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table lm63_dir_table_template[] =
+{
+	{LM63_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_local_temp},
+	{LM63_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_remote_temp},
+	{LM63_SYSCTL_TEMP2_TCRIT, "temp2_crit", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_remote_tcrit},
+	{LM63_SYSCTL_TEMP2_TCRIT_HYST, "temp2_crit_hyst", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_remote_tcrit_hyst},
+	{LM63_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_alarms},
+	{LM63_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_fan},
+	{LM63_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm63_pwm},
+	{0}
+};
+
+/*
+ * Real code
+ */
+
+static int lm63_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm63_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm63_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct lm63_data *data;
+	int err = 0;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "lm63: adapter doesn't support SMBus byte "
+		       "data mode, skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) {
+		printk(KERN_ERR "lm63: Out of memory in lm63_detect\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right before the
+	 * LM63-specific data. The LM63-specific data is pointed to by the
+	 * data field from the i2c_client structure.
+	 */
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm63_driver;
+	new_client->flags = 0;
+
+	/* Default to an LM63 if forced */
+	if (kind == 0)
+		kind = lm63;
+
+	if (kind < 0) { /* must identify */
+		u8 man_id, chip_id, reg_config1, reg_config2;
+		u8 reg_alert_status, reg_alert_mask;
+
+		man_id = i2c_smbus_read_byte_data(new_client,
+			 LM63_REG_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			  LM63_REG_CHIP_ID);
+		reg_config1 = i2c_smbus_read_byte_data(new_client,
+			      LM63_REG_CONFIG1);
+		reg_config2 = i2c_smbus_read_byte_data(new_client,
+			      LM63_REG_CONFIG2);
+		reg_alert_status = i2c_smbus_read_byte_data(new_client,
+				   LM63_REG_ALERT_STATUS);
+		reg_alert_mask = i2c_smbus_read_byte_data(new_client,
+				 LM63_REG_ALERT_MASK);
+
+		if (man_id == 0x01 /* National Semiconductor */
+		 && chip_id == 0x41 /* LM63 */
+		 && (reg_config1 & 0x18) == 0x00
+		 && (reg_config2 & 0xF8) == 0x00
+		 && (reg_alert_status & 0x20) == 0x00
+		 && (reg_alert_mask & 0xA4) == 0xA4) {
+			kind = lm63;
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG "lm63: Unsupported chip "
+			       "(man_id=0x%02X, chip_id=0x%02X)\n",
+			       man_id, chip_id);
+#endif			
+			goto ERROR1;
+		}
+	}
+
+	/* Fill in the remaining client fields */
+	strcpy(new_client->name, "LM63 chip");
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client))) {
+		printk(KERN_ERR "lm63: Failed attaching client\n");
+		goto ERROR1;
+	}
+
+	/* Register a new directory entry */
+	if ((err = i2c_register_entry(new_client, "lm63",
+	     lm63_dir_table_template, THIS_MODULE)) < 0) {
+		printk(KERN_ERR "lm63: Failed registering directory entry\n");
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+
+	/* Initialize the LM63 chip */
+	lm63_init_client(new_client);
+
+	return 0;
+
+ERROR2:
+	i2c_detach_client(new_client);
+ERROR1:
+	kfree(data);
+	return err;
+}
+
+/* Idealy we shouldn't have to initialize anything, since the BIOS
+   should have taken care of everything */
+static void lm63_init_client(struct i2c_client *client)
+{
+	struct lm63_data *data = client->data;
+
+	data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+	data->config_fan = i2c_smbus_read_byte_data(client,
+						    LM63_REG_CONFIG_FAN);
+
+	/* Start converting if needed */
+	if (data->config & 0x40) { /* standby */
+#ifdef DEBUG
+		printk(KERN_DEBUG "lm63: Switching to operational mode");
+#endif
+		data->config &= 0xA7;
+		i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
+					  data->config);
+	}
+
+	/* We may need pwm1_freq before ever updating the client data */
+	data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
+	if (data->pwm1_freq == 0)
+		data->pwm1_freq = 1;
+
+#ifdef DEBUG
+	/* Show some debug info about the LM63 configuration */
+	printk(KERN_DEBUG "lm63: Alert/tach pin configured for %s\n",
+		(data->config & 0x04) ? "tachometer input" :
+		"alert output");
+	printk(KERN_DEBUG "lm63: PWM clock %s kHz, output frequency %u Hz\n",
+		(data->config_fan & 0x08) ? "1.4" : "360",
+		((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
+	printk(KERN_DEBUG "lm63: PWM output active %s, %s mode\n",
+		(data->config_fan & 0x10) ? "low" : "high",
+		(data->config_fan & 0x20) ? "manual" : "auto");
+#endif
+}
+
+static int lm63_detach_client(struct i2c_client *client)
+{
+	struct lm63_data *data = client->data;
+	int err;
+
+	i2c_deregister_entry(data->sysctl_id);
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "lm63: Client deregistration failed, client "
+		       "not detached.\n");
+		return err;
+	}
+
+	kfree(data);
+	return 0;
+}
+
+static  void lm63_update_client(struct i2c_client *client)
+{
+	struct lm63_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ) ||
+	    (jiffies < data->last_updated) ||
+	    !data->valid) {
+		if (data->config & 0x04) { /* tachometer enabled  */
+			/* order matters for fan1_input */
+			data->fan1_input = i2c_smbus_read_byte_data(client,
+					   LM63_REG_TACH_COUNT_LSB) & 0xFC;
+			data->fan1_input |= i2c_smbus_read_byte_data(client,
+					    LM63_REG_TACH_COUNT_MSB) << 8;
+			data->fan1_low = (i2c_smbus_read_byte_data(client,
+					  LM63_REG_TACH_LIMIT_LSB) & 0xFC)
+				       | (i2c_smbus_read_byte_data(client,
+					  LM63_REG_TACH_LIMIT_MSB) << 8);
+		}
+
+		data->pwm1_freq = i2c_smbus_read_byte_data(client,
+				  LM63_REG_PWM_FREQ);
+		if (data->pwm1_freq == 0)
+			data->pwm1_freq = 1;
+		data->pwm1_value = i2c_smbus_read_byte_data(client,
+				   LM63_REG_PWM_VALUE);
+
+		data->temp1_input = i2c_smbus_read_byte_data(client,
+				    LM63_REG_LOCAL_TEMP);
+		data->temp1_high = i2c_smbus_read_byte_data(client,
+				   LM63_REG_LOCAL_HIGH);
+
+		/* order matters for temp2_input */
+		data->temp2_input = i2c_smbus_read_byte_data(client,
+				    LM63_REG_REMOTE_TEMP_MSB) << 8;
+		data->temp2_input |= i2c_smbus_read_byte_data(client,
+				     LM63_REG_REMOTE_TEMP_LSB);
+		data->temp2_high = (i2c_smbus_read_byte_data(client,
+				   LM63_REG_REMOTE_HIGH_MSB) << 8)
+				 | i2c_smbus_read_byte_data(client,
+				   LM63_REG_REMOTE_HIGH_LSB);
+		data->temp2_low = (i2c_smbus_read_byte_data(client,
+				  LM63_REG_REMOTE_LOW_MSB) << 8)
+				| i2c_smbus_read_byte_data(client,
+				  LM63_REG_REMOTE_LOW_LSB);
+		data->temp2_crit = i2c_smbus_read_byte_data(client,
+				   LM63_REG_REMOTE_TCRIT);
+		data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
+					LM63_REG_REMOTE_TCRIT_HYST);
+
+		/* Mask out Busy bit in status register */
+		data->alarms = i2c_smbus_read_byte_data(client,
+			       LM63_REG_ALERT_STATUS) & 0x7F;
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void lm63_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = TEMP8_FROM_REG(data->temp1_high);
+		results[1] = TEMP8_FROM_REG(data->temp1_input);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp1_high = TEMP8_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH,
+				data->temp1_high);
+		}
+	}
+}
+
+static void lm63_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = TEMP11_FROM_REG(data->temp2_high);
+		results[1] = TEMP11_FROM_REG(data->temp2_low);
+		results[2] = TEMP11_FROM_REG(data->temp2_input);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp2_high = TEMP11_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_REMOTE_HIGH_MSB,
+				data->temp2_high >> 8);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_REMOTE_HIGH_LSB,
+				data->temp2_high & 0xFF);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp2_low = TEMP11_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_REMOTE_LOW_MSB,
+				data->temp2_low >> 8);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_REMOTE_LOW_LSB,
+				data->temp2_low & 0xFF);
+		}
+	}
+}
+
+static void lm63_remote_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = TEMP8_FROM_REG(data->temp2_crit);
+		*nrels_mag = 1;
+	}
+}
+
+static void lm63_remote_tcrit_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = TEMP8_FROM_REG(data->temp2_crit) -
+			     TEMP8_FROM_REG(data->temp2_crit_hyst);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp2_crit_hyst = HYST_TO_REG(data->temp2_crit -
+						results[0]);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_REMOTE_TCRIT_HYST,
+				data->temp2_crit_hyst);
+		}
+	}
+}
+
+static void lm63_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+static void lm63_fan(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (!(data->config & 0x04)) { /* tachometer disabled */
+			results[0] = 0;
+			*nrels_mag = 1;
+			return;
+		}
+
+		lm63_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan1_low);
+		results[1] = FAN_FROM_REG(data->fan1_input);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (!(data->config & 0x04)) /* tachometer disabled */
+			return;
+
+		if (*nrels_mag >= 1) {
+			data->fan1_low = FAN_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_TACH_LIMIT_LSB,
+				data->fan1_low & 0xFF);
+			i2c_smbus_write_byte_data(client,
+				LM63_REG_TACH_LIMIT_MSB,
+				data->fan1_low >> 8);
+		}
+	}
+}
+
+static void lm63_pwm(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm63_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm63_update_client(client);
+		results[0] = data->pwm1_value >= 2 * data->pwm1_freq ? 255 :
+			     (data->pwm1_value * 255 + data->pwm1_freq) /
+			     (2 * data->pwm1_freq);
+		results[1] = data->config_fan & 0x20 ? 1 : 2;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1 && (data->config_fan & 0x20)) {
+			data->pwm1_value = results[0] <= 0 ? 0 :
+				results[0] >= 255 ? 2 * data->pwm1_freq :
+				(results[0] * data->pwm1_freq * 2 + 127) / 255;
+			i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE,
+				data->pwm1_value);
+		}
+	}
+}
+
+static int __init lm63_init(void)
+{
+	printk(KERN_INFO "lm63 version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm63_driver);
+}
+
+static void __exit lm63_exit(void)
+{
+	i2c_del_driver(&lm63_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM63 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(lm63_init);
+module_exit(lm63_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1307.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1307.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/ds1307.c	(revision 2867)
@@ -0,0 +1,345 @@
+
+/*
+ * linux/drivers/i2c/ds1307.c
+ *
+ * Author: Abraham van der Merwe <abraham@2d3d.co.za>
+ *
+ * Linux support for the Dallas Semiconductor DS1307 Serial Real-Time
+ * Clock.
+ *
+ * Based on code from the lm-sensors project which is available
+ * at http://www.lm-sensors.nu/ and Russell King's PCF8583 Real-Time
+ * Clock driver (linux/drivers/acorn/char/pcf8583.c).
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <asm/semaphore.h>
+#include "version.h"
+#include "ds1307.h"
+
+#define BCD_TO_BIN(x) (((x) & 15) + ((x) >> 4) * 10)
+#define BIN_TO_BCD(x) ((((x) / 10) << 4) + (x) % 10)
+
+static struct i2c_driver ds1307;
+static DECLARE_MUTEX (mutex);
+
+/*
+ * The DS1307 Real-Time Clock wants the address in a different
+ * message, so we can't use the normal i2c_master_recv() routine
+ * for receiving data.
+ */
+static int ds1307_i2c_recv (struct i2c_client *client,char *buf,char addr,int count)
+{
+	struct i2c_msg msg[] = {
+		{ addr: client->addr, flags: 0,        len: 1,     buf: &addr },
+		{ addr: client->addr, flags: I2C_M_RD, len: count, buf: buf }
+	};
+	int result = 0;
+
+	if (down_interruptible (&mutex))
+		return (-ERESTARTSYS);
+
+	if (i2c_transfer (client->adapter,msg,2) != 2)
+		result = -EIO;
+
+	up (&mutex);
+
+	return (result);
+}
+
+/*
+ * Would've been nice to specify the address to this as well, but then we
+ * would need to copy the buffer twice - not worth it...
+ */
+static int ds1307_i2c_send (struct i2c_client *client,const char *buf,int count)
+{
+	int result = 0;
+
+	if (down_interruptible (&mutex))
+		return (-ERESTARTSYS);
+
+	if (i2c_master_send (client,(const char *) buf,count) != count)
+		result = -EIO;
+
+	up (&mutex);
+
+	return (result);
+}
+
+static int ds1307_attach (struct i2c_adapter *adapter,int addr,unsigned short flags,int kind)
+{
+	struct i2c_client *client;
+	int result;
+
+	if ((client = (struct i2c_client *) kmalloc (sizeof (struct i2c_client),GFP_KERNEL)) == NULL)
+		return (-ENOMEM);
+
+	strcpy (client->name,ds1307.name);
+	client->flags = I2C_CLIENT_ALLOW_USE | I2C_CLIENT_ALLOW_MULTIPLE_USE;
+	client->addr = addr;
+	client->adapter = adapter;
+	client->driver = &ds1307;
+	client->data = NULL;
+
+	if ((result = i2c_attach_client (client))) {
+		kfree (client);
+		return (result);
+	}
+
+	return (0);
+}
+
+static int ds1307_attach_adapter (struct i2c_adapter *adapter)
+{
+	static unsigned short ignore[] = { I2C_CLIENT_END };
+	static unsigned short addr[] = { 0x68, I2C_CLIENT_END };
+	static struct i2c_client_address_data ds1307_addr_data = {
+		normal_i2c:			addr,
+		normal_i2c_range:	ignore,
+		probe:				ignore,
+		probe_range:		ignore,
+		ignore:				ignore,
+		ignore_range:		ignore,
+		force:				ignore
+	};
+
+	return (i2c_probe (adapter,&ds1307_addr_data,ds1307_attach));
+}
+
+static int ds1307_detach_client (struct i2c_client *client)
+{
+	int result;
+
+	if ((result = i2c_detach_client (client)))
+		return (result);
+
+	kfree (client);
+
+	return (0);
+}
+
+static int ds1307_getdate (struct i2c_client *client,void *arg)
+{
+	struct ds1307_date *date = (struct ds1307_date *) arg;
+	u8 buf[7];
+
+	/* this also enables the oscillator */
+	memset (buf,0,7);
+
+	/* enable 24-hour mode */
+	buf[2] = 0x40;
+
+	if (ds1307_i2c_recv (client,(char *) buf,0,7) < 0)
+		return (-EIO);
+
+	date->tm_sec = BCD_TO_BIN (buf[0] & ~0x80);
+	date->tm_min = BCD_TO_BIN (buf[1]);
+	date->tm_hour = BCD_TO_BIN (buf[2] & 0x3f);
+	date->tm_wday = BCD_TO_BIN (buf[3]) - 1;
+	date->tm_mday = BCD_TO_BIN (buf[4]);
+	date->tm_mon = BCD_TO_BIN (buf[5]) - 1;
+	date->tm_year = BCD_TO_BIN (buf[6]) + 100;
+
+	return (0);
+}
+
+static int ds1307_setdate (struct i2c_client *client,void *arg)
+{
+	struct ds1307_date *date = (struct ds1307_date *) arg;
+	u8 buf[8];
+
+	/* select address 0 */
+	buf[0] = 0;
+
+	buf[1] = BIN_TO_BCD (date->tm_sec);
+	buf[2] = BIN_TO_BCD (date->tm_min);
+	buf[3] = BIN_TO_BCD (date->tm_hour) | 0x40;
+	buf[4] = BIN_TO_BCD (date->tm_wday + 1);
+	buf[5] = BIN_TO_BCD (date->tm_mday);
+	buf[6] = BIN_TO_BCD (date->tm_mon + 1);
+	buf[7] = BIN_TO_BCD (date->tm_year - 100);
+
+	return (ds1307_i2c_send (client,(const char *) buf,8));
+}
+
+static int ds1307_enable (struct i2c_client *client,void *arg)
+{
+	u8 buf[2];
+
+	if (ds1307_i2c_recv (client,(char *) buf + 1,0,1) < 0)
+		return (-EIO);
+
+	if ((buf[1] & 0x80)) {
+		buf[0] = 0, buf[1] &= ~0x80;
+		return (ds1307_i2c_send (client,(const char *) buf,2));
+	}
+
+	return (0);
+}
+
+static int ds1307_irqon (struct i2c_client *client,void *arg)
+{
+	u8 buf[2];
+
+	if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
+		return (-EIO);
+
+	buf[0] = 7;
+	buf[1] |= 0x10;
+
+	return (ds1307_i2c_send (client,(const char *) buf,2));
+}
+
+static int ds1307_irqoff (struct i2c_client *client,void *arg)
+{
+	u8 buf[2];
+
+	if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
+		return (-EIO);
+
+	buf[0] = 7;
+	buf[1] &= ~0x10;
+
+	return (ds1307_i2c_send (client,(const char *) buf,2));
+}
+
+static int ds1307_getfreq (struct i2c_client *client,void *arg)
+{
+	u16 *freq = (u16 *) arg;
+	u8 buf;
+	static const u16 table[] = {
+		DS1307_FREQ_1HZ,
+		DS1307_FREQ_4KHZ,
+		DS1307_FREQ_8KHZ,
+		DS1307_FREQ_32KHZ
+	};
+
+	if (ds1307_i2c_recv (client,(char *) &buf,7,1) < 0)
+		return (-EIO);
+
+	*freq = table[buf & 3];
+
+	return (0);
+}
+
+static int ds1307_setfreq (struct i2c_client *client,void *arg)
+{
+	u16 *freq = (u16 *) arg;
+	u8 buf[2];
+
+	/* select address 7 */
+	buf[0] = 7;
+
+	/* default to 1HZ */
+	buf[1] = 0;
+
+	switch (*freq) {
+	case DS1307_FREQ_32KHZ:		buf[1]++;
+	case DS1307_FREQ_8KHZ:		buf[1]++;
+	case DS1307_FREQ_4KHZ:		buf[1]++;
+	case DS1307_FREQ_1HZ:		break;
+	default:
+		return (-EINVAL);
+	}
+
+	return (ds1307_i2c_send (client,(const char *) buf,2));
+}
+
+static int ds1307_read (struct i2c_client *client,void *arg)
+{
+	struct ds1307_memory *mem = (struct ds1307_memory *) arg;
+	u8 buf[DS1307_SIZE];
+
+	if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
+		return (-EINVAL);
+
+	if (ds1307_i2c_recv (client,(char *) buf,mem->offset + 8,mem->length) < 0)
+		return (-EIO);
+
+	memcpy (mem->buf,buf,mem->length);
+
+	return (0);
+}
+
+static int ds1307_write (struct i2c_client *client,void *arg)
+{
+	struct ds1307_memory *mem = (struct ds1307_memory *) arg;
+	u8 buf[DS1307_SIZE + 1];
+
+	if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
+		return (-EINVAL);
+
+	buf[0] = mem->offset + 8;
+
+	memcpy (buf + 1,mem->buf,mem->length);
+
+	return (ds1307_i2c_send (client,(const char *) buf,mem->length + 1));
+}
+
+static int ds1307_command (struct i2c_client *client,unsigned int cmd,void *arg)
+{
+	static const struct {
+		int cmd;
+		int (*function) (struct i2c_client *,void *arg);
+	} ioctl[] = {
+		{ DS1307_ENABLE, ds1307_enable },
+		{ DS1307_GET_DATE, ds1307_getdate },
+		{ DS1307_SET_DATE, ds1307_setdate },
+		{ DS1307_IRQ_ON, ds1307_irqon },
+		{ DS1307_IRQ_OFF, ds1307_irqoff },
+		{ DS1307_GET_FREQ, ds1307_getfreq },
+		{ DS1307_SET_FREQ, ds1307_setfreq },
+		{ DS1307_READ, ds1307_read },
+		{ DS1307_WRITE, ds1307_write }
+	};
+	int i;
+
+	for (i = 0; i < sizeof (ioctl) / sizeof (ioctl[0]); i++)
+		if (ioctl[i].cmd == cmd)
+			return (ioctl[i].function (client,arg));
+
+	return (-EINVAL);
+}
+
+
+static struct i2c_driver ds1307 = {
+	.name		= "ds1307",
+	.id		= I2C_DRIVERID_DS1307,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ds1307_attach_adapter,
+	.detach_client	= ds1307_detach_client,
+	.command	= ds1307_command,
+};
+
+static int __init sm_ds1307_init(void)
+{
+	printk("ds1307.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ds1307);
+}
+
+static void __exit sm_ds1307_exit(void)
+{
+	i2c_del_driver(&ds1307);
+}
+
+
+
+MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
+MODULE_DESCRIPTION ("Linux support for DS1307 Real-Time Clock");
+
+MODULE_LICENSE ("GPL");
+
+module_init(sm_ds1307_init);
+module_exit(sm_ds1307_exit);
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm83.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm83.c	(revision 4048)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm83.c	(revision 4048)
@@ -0,0 +1,528 @@
+/*
+ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
+ *          monitoring
+ * Copyright (C) 2003  Jean Delvare <khali@linux-fr.org>
+ *
+ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
+ * a sensor chip made by National Semiconductor. It reports up to four
+ * temperatures (its own plus up to three external ones) with a 1 deg
+ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
+ * from National's website at:
+ *   http://www.national.com/pf/LM/LM83.html
+ * Since the datasheet omits to give the chip stepping code, I give it
+ * here: 0x03 (at register 0xff).
+ *
+ * Also supports the LM82 temp sensor, which is basically a stripped down
+ * model of the LM83.  Datasheet is here:
+ * http://www.national.com/pf/LM/LM82.html
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+/*
+ * Addresses to scan
+ * Address is selected using 2 three-level pins, resulting in 9 possible
+ * addresses.
+ */
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
+	0x4c, 0x4e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_2(lm83, lm82);
+
+/*
+ * The LM83 registers
+ * Manufacturer ID is 0x01 for National Semiconductor.
+ */
+
+#define LM83_REG_R_MAN_ID        0xFE
+#define LM83_REG_R_CHIP_ID       0xFF
+#define LM83_REG_R_CONFIG        0x03
+#define LM83_REG_W_CONFIG        0x09
+#define LM83_REG_R_STATUS1       0x02
+#define LM83_REG_R_STATUS2       0x35
+#define LM83_REG_R_LOCAL_TEMP    0x00
+#define LM83_REG_R_LOCAL_HIGH    0x05
+#define LM83_REG_W_LOCAL_HIGH    0x0B
+#define LM83_REG_R_REMOTE1_TEMP  0x30
+#define LM83_REG_R_REMOTE1_HIGH  0x38
+#define LM83_REG_W_REMOTE1_HIGH  0x50
+#define LM83_REG_R_REMOTE2_TEMP  0x01
+#define LM83_REG_R_REMOTE2_HIGH  0x07
+#define LM83_REG_W_REMOTE2_HIGH  0x0D
+#define LM83_REG_R_REMOTE3_TEMP  0x31
+#define LM83_REG_R_REMOTE3_HIGH  0x3A
+#define LM83_REG_W_REMOTE3_HIGH  0x52
+#define LM83_REG_R_TCRIT         0x42
+#define LM83_REG_W_TCRIT         0x5A
+
+/*
+ * Conversions and various macros
+ * The LM83 uses signed 8-bit values with LSB = 1 degree Celsius.
+ */
+
+#define TEMP_FROM_REG(val)	(val)
+#define TEMP_TO_REG(val)	((val) <= -50 ? -50 : \
+				 (val) >= 127 ? 127 : (val))
+
+static const u8 LM83_REG_R_TEMP[] = {
+	LM83_REG_R_LOCAL_TEMP,
+	LM83_REG_R_REMOTE1_TEMP,
+	LM83_REG_R_REMOTE2_TEMP,
+	LM83_REG_R_REMOTE3_TEMP
+};
+
+static const u8 LM83_REG_R_HIGH[] = {
+	LM83_REG_R_LOCAL_HIGH,
+	LM83_REG_R_REMOTE1_HIGH,
+	LM83_REG_R_REMOTE2_HIGH,
+	LM83_REG_R_REMOTE3_HIGH
+};
+
+static const u8 LM83_REG_W_HIGH[] = {
+	LM83_REG_W_LOCAL_HIGH,
+	LM83_REG_W_REMOTE1_HIGH,
+	LM83_REG_W_REMOTE2_HIGH,
+	LM83_REG_W_REMOTE3_HIGH
+};
+
+/*
+ * Functions declaration
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter);
+static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind);
+static int lm83_detach_client(struct i2c_client *client);
+static void lm83_update_client(struct i2c_client *client);
+static void lm83_temp(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void lm83_tcrit(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void lm83_alarms(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+ 
+static struct i2c_driver lm83_driver = {
+	.name           = "LM83 sensor driver",
+	.id             = I2C_DRIVERID_LM83,
+	.flags          = I2C_DF_NOTIFY,
+	.attach_adapter = lm83_attach_adapter,
+	.detach_client  = lm83_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm83_data
+{
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	s8 temp[4], temp_high[4], tcrit;
+	u16 alarms; /* bitvector, combined */
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected LM83.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM83_SYSCTL_LOCAL_TEMP    1200
+#define LM83_SYSCTL_REMOTE1_TEMP  1201
+#define LM83_SYSCTL_REMOTE2_TEMP  1202
+#define LM83_SYSCTL_REMOTE3_TEMP  1203
+#define LM83_SYSCTL_TCRIT         1208
+#define LM83_SYSCTL_ALARMS        1210
+
+#define LM83_ALARM_LOCAL_HIGH     0x0040
+#define LM83_ALARM_LOCAL_CRIT     0x0001
+#define LM83_ALARM_REMOTE1_HIGH   0x8000
+#define LM83_ALARM_REMOTE1_CRIT   0x0100
+#define LM83_ALARM_REMOTE1_OPEN   0x2000
+#define LM83_ALARM_REMOTE2_HIGH   0x0010
+#define LM83_ALARM_REMOTE2_CRIT   0x0002
+#define LM83_ALARM_REMOTE2_OPEN   0x0004
+#define LM83_ALARM_REMOTE3_HIGH   0x1000
+#define LM83_ALARM_REMOTE3_CRIT   0x0200
+#define LM83_ALARM_REMOTE3_OPEN   0x0400
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table lm83_dir_table_template[] =
+{
+	{LM83_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE1_TEMP, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE2_TEMP, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE3_TEMP, "temp4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_TCRIT, "tcrit", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_tcrit},
+	{LM83_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_alarms},
+	{0}
+};
+
+static ctl_table lm82_dir_table_template[] =
+{
+	{LM83_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE2_TEMP, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_TCRIT, "tcrit", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_tcrit},
+	{LM83_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_alarms},
+	{0}
+};
+
+/*
+ * Real code
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm83_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct lm83_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter))
+	{
+		printk("lm83.o: Called for an ISA bus adapter, aborting.\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+	{
+#ifdef DEBUG
+		printk("lm83.o: I2C bus doesn't support byte read mode, "
+		       "skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL)))
+	{
+		printk("lm83.o: Out of memory in lm83_detect (new_client).\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right before the
+	 * LM83-specific data. The LM83-specific data is pointed to by the
+	 * data field from the I2C client data.
+	 */
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm83_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip (actually there is only
+	 * one possible kind of chip for now, LM83). A zero kind means that
+	 * the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+
+	/* Default to an LM83 if forced */
+	if (kind == 0)
+		kind = lm83;
+
+	if (kind < 0) /* detection */
+	{
+		if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
+		      & 0xA8) != 0x00)
+		||  ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
+		      & 0x48) != 0x00)
+		||  ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
+		      & 0x41) != 0x00))
+		{
+#ifdef DEBUG
+			printk(KERN_DEBUG "lm83.o: Detection failed at 0x%02x.\n",
+				address);
+#endif
+			goto ERROR1;
+		}
+	}
+
+	if (kind <= 0) /* identification */
+	{
+		u8 man_id, chip_id;
+
+		man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID);
+		if (man_id == 0x01) /* National Semiconductor */
+		{
+			if (chip_id == 0x03)
+				kind = lm83;
+			else if (chip_id == 0x01)
+				kind = lm82;
+		}
+	}
+
+	if (kind <= 0) /* identification failed */
+	{
+		printk("lm83.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == lm83)
+	{
+		type_name = "lm83";
+		client_name = "LM83 chip";
+	}
+	else if (kind == lm82)
+	{
+		type_name = "lm82";
+		client_name = "LM82 chip";
+	}
+	else
+	{
+		printk("lm83.o: Unknown kind %d.\n", kind);
+		goto ERROR1;
+	}
+	
+	/*
+	 * OK, we got a valid chip so we can fill in the remaining client
+	 * fields.
+	 */
+
+	strcpy(new_client->name, client_name);
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer a new client has arrived.
+	 */
+
+	if ((err = i2c_attach_client(new_client)))
+	{
+#ifdef DEBUG
+		printk("lm83.o: Failed attaching client.\n");
+#endif
+		goto ERROR1;
+	}
+
+	/*
+	 * Register a new directory entry.
+	 */
+
+	if ((kind == lm83 && (err = i2c_register_entry(new_client, type_name,
+	     lm83_dir_table_template, THIS_MODULE)) < 0) ||
+	    (kind == lm82 && (err = i2c_register_entry(new_client, type_name,
+	     lm82_dir_table_template, THIS_MODULE)) < 0))
+	{
+#ifdef DEBUG
+		printk("lm83.o: Failed registering directory entry.\n");
+#endif
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+	/*
+	 * Initialize the LM83 chip
+	 * (Nothing to do for this one.)
+	 */
+
+	return 0;
+
+	ERROR2:
+	i2c_detach_client(new_client);
+	ERROR1:
+	kfree(data);
+	return err;
+}
+
+static int lm83_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm83_data *) (client->data))->sysctl_id);
+	if ((err = i2c_detach_client(client)))
+	{
+		printk("lm83.o: Client deregistration failed, client not "
+		       "detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+static void lm83_update_client(struct i2c_client *client)
+{
+	struct lm83_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2) ||
+	    (jiffies < data->last_updated) || !data->valid)
+	{
+		int nr;
+#ifdef DEBUG
+		printk("lm83.o: Updating LM83 data.\n");
+#endif
+		for (nr = 0; nr < 4 ; nr++)
+		{
+			data->temp[nr] =
+				i2c_smbus_read_byte_data(client, LM83_REG_R_TEMP[nr]);
+			data->temp_high[nr] =
+				i2c_smbus_read_byte_data(client, LM83_REG_R_HIGH[nr]);
+		}
+		data->tcrit = i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
+		data->alarms =
+			i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) +
+			(i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void lm83_temp(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+	int nr = ctl_name - LM83_SYSCTL_LOCAL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_high[nr]);
+		results[1] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 2;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->temp_high[nr] = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr],
+					    data->temp_high[nr]);
+		}
+	}
+}
+
+static void lm83_tcrit(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = TEMP_FROM_REG(data->tcrit);
+		*nrels_mag = 1;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->tcrit = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT,
+				data->tcrit);
+		}
+	}
+}
+
+static void lm83_alarms(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_lm83_init(void)
+{
+	printk(KERN_INFO "lm83.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm83_driver);
+}
+
+static void __exit sm_lm83_exit(void)
+{
+	i2c_del_driver(&lm83_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM83 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_lm83_init);
+module_exit(sm_lm83_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca9540.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca9540.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca9540.c	(revision 2867)
@@ -0,0 +1,241 @@
+/*
+ * pca9540.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring
+ * Copyright (c) 2004  Jean Delvare <khali@linux-fr.org>
+ *
+ * Based on pcf8574.c from the same project by Frodo Looijaard,
+ * Philip Edelbrock, Dan Eaton and Aurelien Jarno.
+ *
+ * The PCA9540 is a 2-channel I2C multiplexer made by Philips
+ * Semiconductors. It is controlled via the I2C bus itself.
+ * The SCL/SDA upstream pair fans out to two SCL/SDA downstream
+ * pairs, or channels. Only one SCL/SDA channel is selected at a
+ * time, determined by the contents of the programmable control
+ * register.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x70, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(pca9540);
+
+/* Each client has this additional data */
+struct pca9540_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+
+	u8 control;	/* Register value */
+};
+
+static int pca9540_attach_adapter(struct i2c_adapter *adapter);
+static int pca9540_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int pca9540_detach_client(struct i2c_client *client);
+static void pca9540_update_client(struct i2c_client *client);
+
+static void pca9540_channel(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pca9540_driver = {
+	.name		= "PCA9540 chip driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= pca9540_attach_adapter,
+	.detach_client	= pca9540_detach_client,
+};
+
+
+/* -- SENSORS SYSCTL START -- */
+
+#define PCA9540_SYSCTL_CHANNEL		1000
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table pca9540_dir_table_template[] = {
+	{PCA9540_SYSCTL_CHANNEL, "channel", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &pca9540_channel},
+	{0}
+};
+
+static int pca9540_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, pca9540_detect);
+}
+
+/* This function is called by i2c_detect. */
+int pca9540_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct pca9540_data *data;
+	int err = 0;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet. */
+	if (!(data = kmalloc(sizeof(struct pca9540_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &pca9540_driver;
+	new_client->flags = 0;
+
+	/* The detection is very weak. */
+	if (kind < 0) {
+		u8 reg = i2c_smbus_read_byte(new_client);
+		if ((reg & 0xfa) != 0x00
+		 || reg != i2c_smbus_read_byte(new_client)
+		 || reg != i2c_smbus_read_byte(new_client)
+		 || reg != i2c_smbus_read_byte(new_client))
+			goto ERROR1;
+	}
+
+	kind = pca9540;
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, "PCA9540 chip");
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, "pca9540",
+				    pca9540_dir_table_template,
+				    THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the PCA9540 chip: nothing needed */
+
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR2:
+	i2c_detach_client(new_client);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int pca9540_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct pca9540_data *) (client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "pca9540.o: Client deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static void pca9540_update_client(struct i2c_client *client)
+{
+	struct pca9540_data *data = client->data;
+
+	down(&data->update_lock);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "pca9540.o: Starting update.\n");
+#endif
+	data->control = i2c_smbus_read_byte(client) & 0x07; 
+
+	up(&data->update_lock);
+}
+
+
+void pca9540_channel(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca9540_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		pca9540_update_client(client);
+		switch(data->control) {
+			case 4: results[0] = 1; break;
+			case 5: results[0] = 2; break;
+			default: results[0] = 0;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			switch(results[0]) {
+				case 1: data->control = 4; break;
+				case 2: data->control = 5; break;
+				case 0: data->control = 0; break;
+				default: return; /* invalid value */
+			}
+			i2c_smbus_write_byte(client, data->control);
+		}
+	}
+}
+
+
+static int __init pca9540_init(void)
+{
+	printk("pca9540.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&pca9540_driver);
+}
+
+static void __exit pca9540_exit(void)
+{
+	i2c_del_driver(&pca9540_driver);
+}
+
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("PCA9540 driver");
+
+module_init(pca9540_init);
+module_exit(pca9540_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm85.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm85.c	(revision 3156)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm85.c	(revision 3156)
@@ -0,0 +1,2038 @@
+/*
+    lm85.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
+    Copyright (c) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
+    Copyright (c) 2003        Margit Schubert-While <margitsw@t-online.de>
+
+    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.
+
+    CHANGELOG
+
+    2002-11-13   First patch for LM85 functionality
+    2002-11-18   LM85 functionality mostly done
+    2002-12-02   Adding ADM1027 functionality
+    2002-12-06   Adding ADT7463 functionality
+    2003-01-09   Code cleanup.
+                 Save reserved bits in case they are implemented
+                    in a future chip.  (Solve problem with lockups
+                    on ADM1027 due to chip initialization)
+                 Added chip initialization bypass option
+    2003-02-12   Add THERM asserted counts for ADT7463
+                 Added #ifdef so we can compile against 2.6.5
+                    without updating i2c-ids.h
+    2003-02-17   Prepare for switch to 2.7.0 development
+                 Implement tmin_control for ADT7463
+                 Expose THERM asserted counts to /proc
+                 Code cleanup
+    2003-02-19   Working with Margit and LM_SENSORS developers
+    2003-02-23   Removed chip initialization entirely
+                 Scale voltages in driver at Margit's request
+                 Change PWM from 0-100% to 0-255 per LM sensors standard
+    2003-02-27   Documentation and code cleanups
+                 Added this CHANGELOG
+                 Print additional precision for temperatures and voltages
+                 Many thanks to Margit Schubert-While and Brandt xxxxxx
+                    for help testing this version
+    2003-02-28   More diagnostic messages regarding BIOS setup
+    2003-03-01   Added Interrupt mask register support.
+    2003-03-08   Fixed problem with pseudo 16-bit registers
+                 Cleaned up some compiler warnings.
+                 Fixed problem with Operating Point and THERM counting
+    2003-03-21   Initial support for EMC6D100 and EMC6D101 chips
+    2003-06-30   Add support for EMC6D100 extra voltage inputs.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+#include "sensors_vid.h"
+
+#ifndef I2C_DRIVERID_LM85
+#define I2C_DRIVERID_LM85  1039
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102);
+
+/* Many LM85 constants specified below */
+
+/* The LM85 registers */
+#define LM85_REG_IN(nr) (0x20 + (nr))
+#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2)
+#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2)
+
+#define LM85_REG_TEMP(nr) (0x25 + (nr))
+#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2)
+#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2)
+
+/* Fan speeds are LSB, MSB (2 bytes) */
+#define LM85_REG_FAN(nr) (0x28 + (nr) *2)
+#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2)
+
+#define LM85_REG_PWM(nr) (0x30 + (nr))
+
+#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr))
+
+#define ADT7463_REG_TMIN_CTL1 0x36
+#define ADT7463_REG_TMIN_CTL2 0x37
+#define ADT7463_REG_TMIN_CTL  0x0136
+
+#define LM85_REG_DEVICE 0x3d
+#define LM85_REG_COMPANY 0x3e
+#define LM85_REG_VERSTEP 0x3f
+/* These are the recognized values for the above regs */
+#define LM85_DEVICE_ADX 0x27
+#define LM85_COMPANY_NATIONAL 0x01
+#define LM85_COMPANY_ANALOG_DEV 0x41
+#define LM85_COMPANY_SMSC 0x5c
+#define LM85_VERSTEP_VMASK 0xf0
+#define LM85_VERSTEP_SMASK 0x0f
+#define LM85_VERSTEP_GENERIC 0x60
+#define LM85_VERSTEP_LM85C 0x60
+#define LM85_VERSTEP_LM85B 0x62
+#define LM85_VERSTEP_ADM1027 0x60
+#define LM85_VERSTEP_ADT7463 0x62
+#define LM85_VERSTEP_ADT7463C 0x6A
+#define LM85_VERSTEP_EMC6D100_A0 0x60
+#define LM85_VERSTEP_EMC6D100_A1 0x61
+#define LM85_VERSTEP_EMC6D102 0x65
+
+#define LM85_REG_CONFIG 0x40
+
+#define LM85_REG_ALARM1 0x41
+#define LM85_REG_ALARM2 0x42
+#define LM85_REG_ALARM  0x0141
+
+#define LM85_REG_VID 0x43
+
+/* Automated FAN control */
+#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr))
+#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr))
+#define LM85_REG_AFAN_SPIKE1 0x62
+#define LM85_REG_AFAN_SPIKE2 0x63
+#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr))
+#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr))
+#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr))
+#define LM85_REG_AFAN_HYST1 0x6d
+#define LM85_REG_AFAN_HYST2 0x6e
+
+#define LM85_REG_TACH_MODE 0x74
+#define LM85_REG_SPINUP_CTL 0x75
+
+#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr))
+#define ADM1027_REG_CONFIG2 0x73
+#define ADM1027_REG_INTMASK1 0x74
+#define ADM1027_REG_INTMASK2 0x75
+#define ADM1027_REG_INTMASK  0x0174
+#define ADM1027_REG_EXTEND_ADC1 0x76
+#define ADM1027_REG_EXTEND_ADC2 0x77
+#define ADM1027_REG_EXTEND_ADC  0x0176
+#define ADM1027_REG_CONFIG3 0x78
+#define ADM1027_REG_FAN_PPR 0x7b
+
+#define ADT7463_REG_THERM 0x79
+#define ADT7463_REG_THERM_LIMIT 0x7A
+#define ADT7463_REG_CONFIG4 0x7D
+
+#define EMC6D100_REG_SFR  0x7c
+#define EMC6D100_REG_ALARM3  0x7d
+#define EMC6D100_REG_CONF  0x7f
+#define EMC6D100_REG_INT_EN  0x80
+/* IN5, IN6 and IN7 */
+#define EMC6D100_REG_IN(nr)  (0x70 + ((nr)-5))
+#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2)
+#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2)
+
+/* 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.
+ */
+
+/* IN are scaled 1.000 == 0xc0, mag = 3 */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255))
+#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0)
+#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0))
+
+/* IN are scaled acording to built-in resistors */
+static int lm85_scaling[] = {  /* .001 Volts */
+		2500, 2250, 3300, 5000, 12000,
+		3300, 1500, 1800,	/* EMC6D100 */
+	};
+#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from))
+#define INS_TO_REG(n,val)  (SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255))
+#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,lm85_scaling[n]))
+#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0))
+
+/* FAN speed is measured using 90kHz clock */
+#define FAN_TO_REG(val)  (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534))
+#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val))
+
+/* Temperature is reported in .01 degC increments */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+50)/100,-127,127))
+#define TEMPEXT_FROM_REG(val,ext) ((val)*100 + (ext)*25)
+#define TEMP_FROM_REG(val) (TEMPEXT_FROM_REG(val,0))
+#define EXTTEMP_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
+#define OPPOINT_TO_REG(val) (SENSORS_LIMIT(val,-127,127))
+#define OPPOINT_FROM_REG(val) (val)
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255))
+#define PWM_FROM_REG(val) (val)
+
+#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03)
+
+/* ZONEs have the following parameters:
+ *    Limit (low) temp,           1. degC
+ *    Hysteresis (below limit),   1. degC (0-15)
+ *    Range of speed control,     .1 degC (2-80)
+ *    Critical (high) temp,       1. degC
+ *
+ * FAN PWMs have the following parameters:
+ *    Reference Zone,                 1, 2, 3, etc.
+ *    Spinup time,                    .05 sec
+ *    PWM value at limit/low temp,    1 count
+ *    PWM Frequency,                  1. Hz
+ *    PWM is Min or OFF below limit,  flag
+ *    Invert PWM output,              flag
+ *
+ * Some chips filter the temp, others the fan.
+ *    Filter constant (or disabled)   .1 seconds
+ */
+
+/* These are the zone temperature range encodings */
+static int lm85_range_map[] = {   /* .1 degC */
+		 20,  25,  33,  40,  50,  66,
+		 80, 100, 133, 160, 200, 266,
+		320, 400, 533, 800
+	};
+static int RANGE_TO_REG( int range )
+{
+	int i;
+
+	if( range >= lm85_range_map[15] ) { return 15 ; }
+	for( i = 0 ; i < 15 ; ++i )
+		if( range <= lm85_range_map[i] )
+			break ;
+	return( i & 0x0f );
+}
+#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f])
+
+/* These are the Acoustic Enhancement, or Temperature smoothing encodings
+ * NOTE: The enable/disable bit is INCLUDED in these encodings as the
+ *       MSB (bit 3, value 8).  If the enable bit is 0, the encoded value
+ *       is ignored, or set to 0.
+ */
+static int lm85_smooth_map[] = {  /* .1 sec */
+		350, 176, 118,  70,  44,   30,   16,    8
+/*    35.4 *    1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48  */
+	};
+static int SMOOTH_TO_REG( int smooth )
+{
+	int i;
+
+	if( smooth <= 0 ) { return 0 ; }  /* Disabled */
+	for( i = 0 ; i < 7 ; ++i )
+		if( smooth >= lm85_smooth_map[i] )
+			break ;
+	return( (i & 0x07) | 0x08 );
+}
+#define SMOOTH_FROM_REG(val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0)
+
+/* These are the fan spinup delay time encodings */
+static int lm85_spinup_map[] = {  /* .1 sec */
+		0, 1, 2, 4, 7, 10, 20, 40
+	};
+static int SPINUP_TO_REG( int spinup )
+{
+	int i;
+
+	if( spinup >= lm85_spinup_map[7] ) { return 7 ; }
+	for( i = 0 ; i < 7 ; ++i )
+		if( spinup <= lm85_spinup_map[i] )
+			break ;
+	return( i & 0x07 );
+}
+#define SPINUP_FROM_REG(val) (lm85_spinup_map[(val)&0x07])
+
+/* These are the PWM frequency encodings */
+static int lm85_freq_map[] = { /* .1 Hz */
+		100, 150, 230, 300, 380, 470, 620, 980
+	};
+static int FREQ_TO_REG( int freq )
+{
+	int i;
+
+	if( freq >= lm85_freq_map[7] ) { return 7 ; }
+	for( i = 0 ; i < 7 ; ++i )
+		if( freq <= lm85_freq_map[i] )
+			break ;
+	return( i & 0x07 );
+}
+#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07])
+
+/* Since we can't use strings, I'm abusing these numbers
+ *   to stand in for the following meanings:
+ *      1 -- PWM responds to Zone 1
+ *      2 -- PWM responds to Zone 2
+ *      3 -- PWM responds to Zone 3
+ *     23 -- PWM responds to the higher temp of Zone 2 or 3
+ *    123 -- PWM responds to highest of Zone 1, 2, or 3
+ *      0 -- PWM is always at 0% (ie, off)
+ *     -1 -- PWM is always at 100%
+ *     -2 -- PWM responds to manual control
+ */
+static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 };
+static int ZONE_TO_REG( int zone )
+{
+	int i;
+
+	for( i = 0 ; i <= 7 ; ++i )
+		if( zone == lm85_zone_map[i] )
+			break ;
+	if( i > 7 )   /* Not found. */
+		i = 3;  /* Always 100% */
+	return( (i & 0x07)<<5 );
+}
+#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07])
+
+#define HYST_TO_REG(val) (SENSORS_LIMIT((-(val)+5)/10,0,15))
+#define HYST_FROM_REG(val) (-(val)*10)
+
+#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
+#define OFFSET_FROM_REG(val) ((val)*25)
+
+#define PPR_MASK(fan) (0x03<<(fan *2))
+#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2))
+#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1)
+
+/* When converting to REG, we need to fixup the carry-over bit */
+#define INTMASK_FROM_REG(val) (val)
+#define INTMASK_TO_REG(val) (SENSORS_LIMIT((val)|((val)&0xff00?0x80:0),0,65535))
+
+/* Typically used with Pentium 4 systems v9.1 VRM spec */
+#define LM85_INIT_VRM  91
+
+/* Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ *    so it doesn't make sense to read them more often than that.
+ *    We cache the results and return the saved data if the driver
+ *    is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ *    given the automatic PWM fan control that is possible.  There
+ *    are about 47 bytes of config data to only 22 bytes of actual
+ *    readings.  So, we keep the config data up to date in the cache
+ *    when it is written and only sample it once every 5 *minutes*
+ */
+#define LM85_DATA_INTERVAL  (1 * HZ)
+#define LM85_CONFIG_INTERVAL  (5 * 60 * HZ)
+
+/* For each registered LM85, we need to keep some data in memory. That
+   data is pointed to by client->data. The structure itself is
+   dynamically allocated, when a new lm85 client is allocated. */
+
+/* LM85 can automatically adjust fan speeds based on temperature
+ * This structure encapsulates an entire Zone config.  There are
+ * three zones (one for each temperature input) on the lm85
+ */
+struct lm85_zone {
+	s8 limit;	/* Low temp limit */
+	u8 hyst;	/* Low limit hysteresis. (0-15) */
+	u8 range;	/* Temp range, encoded */
+	s8 critical;	/* "All fans ON" temp limit */
+};
+
+struct lm85_autofan {
+	u8 config;	/* Register value */
+	u8 freq;	/* PWM frequency, encoded */
+	u8 min_pwm;	/* Minimum PWM value, encoded */
+	u8 min_off;	/* Min PWM or OFF below "limit", flag */
+};
+
+struct lm85_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	int valid;		/* !=0 if following fields are valid */
+	unsigned long last_reading;	/* In jiffies */
+	unsigned long last_config;	/* In jiffies */
+
+	u8 in[8];		/* Register value */
+	u8 in_max[8];		/* Register value */
+	u8 in_min[8];		/* Register value */
+	s8 temp[3];		/* Register value */
+	s8 temp_min[3];		/* Register value */
+	s8 temp_max[3];		/* Register value */
+	s8 temp_offset[3];	/* Register value */
+	u16 fan[4];		/* Register value */
+	u16 fan_min[4];		/* Register value */
+	u8 pwm[3];		/* Register value */
+	u8 spinup_ctl;		/* Register encoding, combined */
+	u8 tach_mode;		/* Register encoding, combined */
+	u16 extend_adc;		/* Register value */
+	u8 fan_ppr;		/* Register value */
+	u8 smooth[3];		/* Register encoding */
+	u8 vid;			/* Register value */
+	u8 vrm;			/* VRM version */
+	u8 syncpwm3;		/* Saved PWM3 for TACH 2,3,4 config */
+	s8 oppoint[3];		/* Register value */
+	u16 tmin_ctl;		/* Register value */
+	long therm_total;	/* Cummulative therm count */
+	long therm_ovfl;	/* Count of therm overflows */
+	u8 therm_limit;		/* Register value */
+	u32 alarms;		/* Register encoding, combined */
+	u32 alarm_mask;		/* Register encoding, combined */
+	struct lm85_autofan autofan[3];
+	struct lm85_zone zone[3];
+};
+
+static int lm85_attach_adapter(struct i2c_adapter *adapter);
+static int lm85_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static int lm85_detach_client(struct i2c_client *client);
+static int lm85_read_value(struct i2c_client *client, u16 register);
+static int lm85_write_value(struct i2c_client *client, u16 register, int value);
+static void lm85_update_client(struct i2c_client *client);
+static void lm85_init_client(struct i2c_client *client);
+
+
+static void lm85_in(struct i2c_client *client, int operation, int ctl_name,
+			int *nrels_mag, long *results);
+static void lm85_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_zone(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm_config(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm_zone(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_smooth(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void lm85_spinup_ctl(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_tach_mode(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void adm1027_tach_mode(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_temp_offset(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_fan_ppr(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_alarm_mask(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void adt7463_tmin_ctl(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adt7463_therm_signal(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void emc6d100_in(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver lm85_driver = {
+	.name		=  "LM85 compatible sensor driver",
+	.id		= I2C_DRIVERID_LM85,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= &lm85_attach_adapter,
+	.detach_client	= &lm85_detach_client,
+};
+
+/* Unique ID assigned to each LM85 detected */
+static int lm85_id = 0;
+
+/* -- SENSORS SYSCTL START -- */
+/* Common parameters */
+#define LM85_SYSCTL_IN0                1000
+#define LM85_SYSCTL_IN1                1001
+#define LM85_SYSCTL_IN2                1002
+#define LM85_SYSCTL_IN3                1003
+#define LM85_SYSCTL_IN4                1004
+#define LM85_SYSCTL_FAN1               1005
+#define LM85_SYSCTL_FAN2               1006
+#define LM85_SYSCTL_FAN3               1007
+#define LM85_SYSCTL_FAN4               1008
+#define LM85_SYSCTL_TEMP1              1009
+#define LM85_SYSCTL_TEMP2              1010
+#define LM85_SYSCTL_TEMP3              1011
+#define LM85_SYSCTL_VID                1012
+#define LM85_SYSCTL_ALARMS             1013
+#define LM85_SYSCTL_PWM1               1014
+#define LM85_SYSCTL_PWM2               1015
+#define LM85_SYSCTL_PWM3               1016
+#define LM85_SYSCTL_VRM                1017
+#define LM85_SYSCTL_PWM_CFG1           1019
+#define LM85_SYSCTL_PWM_CFG2           1020
+#define LM85_SYSCTL_PWM_CFG3           1021
+#define LM85_SYSCTL_PWM_ZONE1          1022
+#define LM85_SYSCTL_PWM_ZONE2          1023
+#define LM85_SYSCTL_PWM_ZONE3          1024
+#define LM85_SYSCTL_ZONE1              1025
+#define LM85_SYSCTL_ZONE2              1026
+#define LM85_SYSCTL_ZONE3              1027
+#define LM85_SYSCTL_SMOOTH1            1028
+#define LM85_SYSCTL_SMOOTH2            1029
+#define LM85_SYSCTL_SMOOTH3            1030
+
+/* Vendor specific values */
+#define LM85_SYSCTL_SPINUP_CTL         1100
+#define LM85_SYSCTL_TACH_MODE          1101
+
+/* Analog Devices variant of the LM85 */
+#define ADM1027_SYSCTL_TACH_MODE       1200
+#define ADM1027_SYSCTL_TEMP_OFFSET1    1201
+#define ADM1027_SYSCTL_TEMP_OFFSET2    1202
+#define ADM1027_SYSCTL_TEMP_OFFSET3    1203
+#define ADM1027_SYSCTL_FAN_PPR         1204
+#define ADM1027_SYSCTL_ALARM_MASK      1205
+
+/* Analog Devices variant of the LM85/ADM1027 */
+#define ADT7463_SYSCTL_TMIN_CTL1       1300
+#define ADT7463_SYSCTL_TMIN_CTL2       1301
+#define ADT7463_SYSCTL_TMIN_CTL3       1302
+#define ADT7463_SYSCTL_THERM_SIGNAL    1303
+
+/* SMSC variant of the LM85 */
+#define EMC6D100_SYSCTL_IN5            1400
+#define EMC6D100_SYSCTL_IN6            1401
+#define EMC6D100_SYSCTL_IN7            1402
+
+#define LM85_ALARM_IN0          0x0001
+#define LM85_ALARM_IN1          0x0002
+#define LM85_ALARM_IN2          0x0004
+#define LM85_ALARM_IN3          0x0008
+#define LM85_ALARM_TEMP1        0x0010
+#define LM85_ALARM_TEMP2        0x0020
+#define LM85_ALARM_TEMP3        0x0040
+#define LM85_ALARM_ALARM2       0x0080
+#define LM85_ALARM_IN4          0x0100
+#define LM85_ALARM_RESERVED     0x0200
+#define LM85_ALARM_FAN1         0x0400
+#define LM85_ALARM_FAN2         0x0800
+#define LM85_ALARM_FAN3         0x1000
+#define LM85_ALARM_FAN4         0x2000
+#define LM85_ALARM_TEMP1_FAULT  0x4000
+#define LM85_ALARM_TEMP3_FAULT 0x08000
+#define LM85_ALARM_IN6         0x10000
+#define LM85_ALARM_IN7         0x20000
+#define LM85_ALARM_IN5         0x40000
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected LM85. This is just a template;
+ *    The actual list is built from this and additional per-chip
+ *    custom lists below.  Note the XXX_LEN macros.  These must be
+ *    compile time constants because they will be used to allocate
+ *    space for the final template passed to i2c_register_entry.
+ *    We depend on the ability of GCC to evaluate expressions at
+ *    compile time to turn these expressions into compile time
+ *    constants, but this can generate a warning.
+ */
+static ctl_table lm85_common[] = {
+	{LM85_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_vid},
+	{LM85_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_vrm},
+	{LM85_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_alarms},
+	{LM85_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM_CFG1, "pwm1_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_CFG2, "pwm2_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_CFG3, "pwm3_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_ZONE1, "pwm1_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_PWM_ZONE2, "pwm2_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_PWM_ZONE3, "pwm3_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_ZONE1, "zone1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_ZONE2, "zone2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_ZONE3, "zone3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_SMOOTH1, "smooth1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{LM85_SYSCTL_SMOOTH2, "smooth2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{LM85_SYSCTL_SMOOTH3, "smooth3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{0}
+};
+#define CTLTBL_COMMON (sizeof(lm85_common)/sizeof(lm85_common[0]))
+
+/* NOTE: tach_mode is a shared name, but implemented with
+ *   different functions
+ */
+static ctl_table lm85_specific[] = {
+	{LM85_SYSCTL_SPINUP_CTL, "spinup_ctl", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_spinup_ctl},
+	{LM85_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_tach_mode},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_LM85 (sizeof(lm85_specific)/sizeof(lm85_specific[0]))
+
+static ctl_table adm1027_specific[] = {
+	{ADM1027_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_tach_mode},
+	{ADM1027_SYSCTL_TEMP_OFFSET1, "temp1_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_TEMP_OFFSET2, "temp2_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_TEMP_OFFSET3, "temp3_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_FAN_PPR, "fan_ppr", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_fan_ppr},
+	{ADM1027_SYSCTL_ALARM_MASK, "alarm_mask", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_alarm_mask},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_ADM1027 (sizeof(adm1027_specific)/sizeof(adm1027_specific[0]))
+
+static ctl_table adt7463_specific[] = {
+	{ADT7463_SYSCTL_TMIN_CTL1, "tmin_ctl1", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_TMIN_CTL2, "tmin_ctl2", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_TMIN_CTL3, "tmin_ctl3", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_THERM_SIGNAL, "therm_signal", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_therm_signal},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_ADT7463 (sizeof(adt7463_specific)/sizeof(adt7463_specific[0]))
+
+static ctl_table emc6d100_specific[] = {
+	{EMC6D100_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+	{EMC6D100_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+	{EMC6D100_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_EMC6D100 (sizeof(emc6d100_specific)/sizeof(emc6d100_specific[0]))
+
+
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MAX3(a,b,c) ((a)>(b)?MAX2((a),(c)):MAX2((b),(c)))
+#define MAX4(a,b,c,d) ((a)>(b)?MAX3((a),(c),(d)):MAX3((b),(c),(d)))
+
+#define CTLTBL_MAX (CTLTBL_COMMON + MAX3(CTLTBL_LM85, CTLTBL_ADM1027+CTLTBL_ADT7463, CTLTBL_EMC6D100))
+
+/* This function is called when:
+     * lm85_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and lm85_driver is still present) */
+static int lm85_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm85_detect);
+}
+
+/* This function is called by i2c_detect */
+static int lm85_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind)
+{
+	int i;
+	int company, verstep ;
+	struct i2c_client *new_client;
+	struct lm85_data *data;
+	int err = 0;
+	const char *type_name = "";
+	struct ctl_table template[CTLTBL_MAX] ;
+	int template_used ;
+
+	if (i2c_is_isa_adapter(adapter)) {
+		/* This chip has no ISA interface */
+		goto ERROR0 ;
+	};
+
+	if (!i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		/* We need to be able to do byte I/O */
+		goto ERROR0 ;
+	};
+
+	/* 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 lm85_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct lm85_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm85_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	company = lm85_read_value(new_client, LM85_REG_COMPANY);
+	verstep = lm85_read_value(new_client, LM85_REG_VERSTEP);
+
+#ifdef DEBUG
+	printk("lm85: Detecting device at %d,0x%02x with"
+		" COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+		i2c_adapter_id(new_client->adapter), new_client->addr,
+		company, verstep
+	    );
+#endif
+
+	/* If auto-detecting, Determine the chip type. */
+	if (kind <= 0) {
+#ifdef DEBUG
+		printk("lm85: Autodetecting device at %d,0x%02x ...\n",
+			i2c_adapter_id(adapter), address );
+#endif
+		if( company == LM85_COMPANY_NATIONAL
+		    && verstep == LM85_VERSTEP_LM85C ) {
+			kind = lm85c ;
+		} else if( company == LM85_COMPANY_NATIONAL
+		    && verstep == LM85_VERSTEP_LM85B ) {
+			kind = lm85b ;
+		} else if( company == LM85_COMPANY_NATIONAL
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected National Semiconductor chip\n");
+			printk("lm85: Unrecgonized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && verstep == LM85_VERSTEP_ADM1027 ) {
+			kind = adm1027 ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && (verstep == LM85_VERSTEP_ADT7463
+			 || verstep == LM85_VERSTEP_ADT7463C) ) {
+			kind = adt7463 ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected Analog Devices chip\n");
+			printk("lm85: Unrecgonized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( company == LM85_COMPANY_SMSC
+		    && verstep == LM85_VERSTEP_EMC6D102) {
+			kind = emc6d102;
+		} else if( company == LM85_COMPANY_SMSC
+		    && (verstep == LM85_VERSTEP_EMC6D100_A0
+			 || verstep == LM85_VERSTEP_EMC6D100_A1) ) {
+			/* Unfortunately, we can't tell a '100 from a '101
+			 *   from the registers.  Since a '101 is a '100
+			 *   in a package with fewer pins and therefore no
+			 *   3.3V, 1.5V or 1.8V inputs, perhaps if those
+			 *   inputs read 0, then it's a '101.
+			 */
+			kind = emc6d100 ;
+		} else if( company == LM85_COMPANY_SMSC
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected SMSC chip\n");
+			printk("lm85: Unrecognized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( kind == any_chip
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Generic LM85 Version 6 detected\n");
+			/* Leave kind as "any_chip" */
+		} else {
+#ifdef DEBUG
+			printk("lm85: Autodetection failed\n");
+#endif
+			/* Not an LM85 ... */
+			if( kind == any_chip ) {  /* User used force=x,y */
+			    printk("lm85: Generic LM85 Version 6 not"
+				" found at %d,0x%02x. Try force_lm85c.\n",
+				i2c_adapter_id(adapter), address );
+			}
+			goto ERROR1;
+		}
+	}
+
+	/* Fill in the chip specific driver values */
+	switch (kind) {
+	case any_chip :
+		type_name = "lm85";
+		strcpy(new_client->name, "Generic LM85");
+		template_used = 0 ;
+		break ;
+	case lm85b :
+		type_name = "lm85b";
+		strcpy(new_client->name, "National LM85-B");
+		memcpy( template, lm85_specific, sizeof(lm85_specific) );
+		template_used = CTLTBL_LM85 ;
+		break ;
+	case lm85c :
+		type_name = "lm85c";
+		strcpy(new_client->name, "National LM85-C");
+		memcpy( template, lm85_specific, sizeof(lm85_specific) );
+		template_used = CTLTBL_LM85 ;
+		break ;
+	case adm1027 :
+		type_name = "adm1027";
+		strcpy(new_client->name, "Analog Devices ADM1027");
+		memcpy( template, adm1027_specific, sizeof(adm1027_specific) );
+		template_used = CTLTBL_ADM1027 ;
+		break ;
+	case adt7463 :
+		type_name = "adt7463";
+		strcpy(new_client->name, "Analog Devices ADT7463");
+		memcpy( template, adt7463_specific, sizeof(adt7463_specific) );
+		template_used = CTLTBL_ADT7463 ;
+		memcpy( template+template_used, adm1027_specific, sizeof(adm1027_specific) );
+		template_used += CTLTBL_ADM1027 ;
+		break ;
+	case emc6d100 :
+		type_name = "emc6d100";
+		strcpy(new_client->name, "SMSC EMC6D100");
+		memcpy(template, emc6d100_specific, sizeof(emc6d100_specific));
+		template_used = CTLTBL_EMC6D100 ;
+		break ;
+	case emc6d102 :
+		type_name = "emc6d102";
+		strcpy(new_client->name, "SMSC EMC6D102");
+		template_used = 0;
+		break;
+	default :
+		printk("lm85: Internal error, invalid kind (%d)!", kind);
+		err = -EFAULT ;
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields */
+	new_client->id = lm85_id++;
+	printk("lm85: Assigning ID %d to %s at %d,0x%02x\n",
+		new_client->id, new_client->name,
+		i2c_adapter_id(new_client->adapter),
+		new_client->addr
+	    );
+
+	/* Housekeeping values */
+	data->type = kind;
+	data->valid = 0;
+
+	/* Set the VRM version */
+	data->vrm = LM85_INIT_VRM ;
+
+	/* Zero the accumulators */
+	data->therm_total = 0;
+	data->therm_ovfl = 0;
+
+	init_MUTEX(&data->update_lock);
+
+	/* Initialize the LM85 chip */
+	lm85_init_client(new_client);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Finish out the template */
+	memcpy( template + template_used, lm85_common, sizeof(lm85_common) );
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+	/* Error out and cleanup code */
+    ERROR2:
+	i2c_detach_client(new_client);
+    ERROR1:
+	kfree(data);
+    ERROR0:
+	return err;
+}
+
+static int lm85_detach_client(struct i2c_client *client)
+{
+	int err;
+	int id ;
+
+	id = client->id;
+	i2c_deregister_entry(((struct lm85_data *)(client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("lm85(%d): Client deregistration failed,"
+			" client not detached.\n", id );
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int lm85_read_value(struct i2c_client *client, u16 reg)
+{
+	int res;
+
+	/* What size location is it? */
+	switch( reg ) {
+	case LM85_REG_FAN(0) :  /* Read WORD data */
+	case LM85_REG_FAN(1) :
+	case LM85_REG_FAN(2) :
+	case LM85_REG_FAN(3) :
+	case LM85_REG_FAN_MIN(0) :
+	case LM85_REG_FAN_MIN(1) :
+	case LM85_REG_FAN_MIN(2) :
+	case LM85_REG_FAN_MIN(3) :
+	case LM85_REG_ALARM :  /* Read ALARM1 and ALARM2 */
+	case ADM1027_REG_INTMASK :  /* Read MASK1 and MASK2 */
+	case ADM1027_REG_EXTEND_ADC :  /* Read ADC1 and ADC2 */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_read_byte_data(client, reg) & 0xff ;
+		res |= (i2c_smbus_read_byte_data(client, reg+1) & 0xff) << 8 ;
+		break ;
+	case ADT7463_REG_TMIN_CTL :  /* Read WORD MSB, LSB */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = (i2c_smbus_read_byte_data(client, reg) & 0xff) << 8 ;
+		res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ;
+		break ;
+	default:	/* Read BYTE data */
+		res = i2c_smbus_read_byte_data(client, reg & 0xff) & 0xff ;
+		break ;
+	}
+
+	return res ;
+}
+
+static int lm85_write_value(struct i2c_client *client, u16 reg, int value)
+{
+	int res ;
+
+	switch( reg ) {
+	case LM85_REG_FAN(0) :  /* Write WORD data */
+	case LM85_REG_FAN(1) :
+	case LM85_REG_FAN(2) :
+	case LM85_REG_FAN(3) :
+	case LM85_REG_FAN_MIN(0) :
+	case LM85_REG_FAN_MIN(1) :
+	case LM85_REG_FAN_MIN(2) :
+	case LM85_REG_FAN_MIN(3) :
+	case ADM1027_REG_INTMASK :
+	/* NOTE: ALARM and ADC are read only, so not included here */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ;
+		res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ;
+		break ;
+	case ADT7463_REG_TMIN_CTL :  /* Write WORD MSB, LSB */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff);
+		res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ;
+		break ;
+	default:	/* Write BYTE data */
+		res = i2c_smbus_write_byte_data(client, reg & 0xff, value);
+		break ;
+	}
+
+	return res ;
+}
+
+/* Called when we have found a new LM85. */
+static void lm85_init_client(struct i2c_client *client)
+{
+	int value;
+	struct lm85_data *data = client->data;
+
+#ifdef DEBUG
+	printk("lm85(%d): Initializing device\n", client->id);
+#endif
+
+	/* Warn if part was not "READY" */
+	value = lm85_read_value(client, LM85_REG_CONFIG);
+#ifdef DEBUG
+	printk("lm85(%d): LM85_REG_CONFIG is: 0x%02x\n", client->id, value );
+#endif
+	if( value & 0x02 ) {
+		printk("lm85(%d): Client (%d,0x%02x) config is locked.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+	if( ! (value & 0x04) ) {
+		printk("lm85(%d): Client (%d,0x%02x) is not ready.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+	if( (data->type == adm1027 || data->type == adt7463)
+	    && (value & 0x10)
+	) {
+		printk("lm85(%d): Client (%d,0x%02x) VxI mode is set.  "
+			"Please report this to the lm85 maintainer.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+
+	/* See if SYNC to PWM3 is set */
+	if( data->type == adt7463 
+	    && (lm85_read_value(client, LM85_REG_AFAN_SPIKE1) & 0x10)
+	) {
+		printk("lm85(%d): Sync to PWM3 is set.  Expect PWM3 "
+			"to control fans 2, 3, and 4\n",
+			client->id );
+	};
+
+	/* See if PWM2 is #SMBALERT */
+	if( (data->type == adm1027 || data->type == adt7463)
+	    && (lm85_read_value(client, ADM1027_REG_CONFIG3) & 0x01)
+	) {
+		printk("lm85(%d): PWM2 is SMBALERT.  PWM2 not available.\n",
+			client->id );
+	};
+
+	/* Check if 2.5V and 5V inputs are reconfigured */
+	if( data->type == adt7463 ) {
+		value = lm85_read_value(client, ADT7463_REG_CONFIG4);
+		if( value & 0x01 ) {
+			printk("lm85(%d): 2.5V input (in0) is SMBALERT.  "
+				"in0 not available.\n", client->id );
+		};
+		if( value & 0x02 ) {
+			printk("lm85(%d): 5V input (in3) is THERM.  "
+				"in3 not available.\n", client->id );
+		}
+	};
+
+	/* FIXME?  Display EMC6D100 config info? */
+
+	/* WE INTENTIONALLY make no changes to the limits,
+	 *   offsets, pwms, fans and zones.  If they were
+	 *   configured, we don't want to mess with them.
+	 *   If they weren't, the default is 100% PWM, no
+	 *   control and will suffice until 'sensors -s'
+	 *   can be run by the user.
+	 */
+
+	/* Start monitoring */
+	value = lm85_read_value(client, LM85_REG_CONFIG);
+	/* Try to clear LOCK, Set START, save everything else */
+	value = ((value & ~ 0x02) | 0x01) & 0xff ;
+#ifdef DEBUG
+	printk("lm85(%d): Setting CONFIG to: 0x%02x\n", client->id, value );
+#endif
+	lm85_write_value(client, LM85_REG_CONFIG, value);
+
+}
+
+static void lm85_update_client(struct i2c_client *client)
+{
+	struct lm85_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if (!data->valid
+	    || (jiffies - data->last_reading > LM85_DATA_INTERVAL )) {
+		/* Things that change quickly */
+
+#ifdef DEBUG
+		printk("lm85(%d): Reading sensor values\n", client->id);
+#endif
+		/* Have to read extended bits first to "freeze" the
+		 * more significant bits that are read later.
+		 */
+		switch( data->type ) {
+		case adm1027 :
+		case adt7463 :
+			data->extend_adc =
+			    lm85_read_value(client, ADM1027_REG_EXTEND_ADC);
+			break ;
+		default :
+			data->extend_adc = 0 ;
+			break ;
+		}
+
+		for (i = 0; i <= 4; ++i) {
+			data->in[i] =
+			    lm85_read_value(client, LM85_REG_IN(i));
+		}
+
+		for (i = 0; i <= 3; ++i) {
+			data->fan[i] =
+			    lm85_read_value(client, LM85_REG_FAN(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->temp[i] =
+			    lm85_read_value(client, LM85_REG_TEMP(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->pwm[i] =
+			    lm85_read_value(client, LM85_REG_PWM(i));
+		}
+
+		data->alarms = lm85_read_value(client, LM85_REG_ALARM);
+
+		switch( ((struct lm85_data *)(client->data))->type ) {
+		case adt7463 :
+			/* REG_THERM code duplicated in therm_signal() */
+			i = lm85_read_value(client, ADT7463_REG_THERM);
+			if( data->therm_total < LONG_MAX - 256 ) {
+			    data->therm_total += i ;
+			}
+			if( i >= 255 ) {
+				++data->therm_ovfl ;
+			}
+			break ;
+		case emc6d100 :
+			/* Three more voltage sensors */
+			for (i = 5; i <= 7; ++i) {
+			    data->in[i] =
+				lm85_read_value(client, EMC6D100_REG_IN(i));
+			}
+			/* More alarm bits */
+			data->alarms |=
+			    lm85_read_value(client, EMC6D100_REG_ALARM3) << 16;
+
+			break ;
+		default : break ; /* no warnings */
+		}
+
+		data->last_reading = jiffies ;
+	};  /* last_reading */
+
+	if (!data->valid
+	    || (jiffies - data->last_config > LM85_CONFIG_INTERVAL) ) {
+		/* Things that don't change often */
+
+#ifdef DEBUG
+		printk("lm85(%d): Reading config values\n", client->id);
+#endif
+		for (i = 0; i <= 4; ++i) {
+			data->in_min[i] =
+			    lm85_read_value(client, LM85_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm85_read_value(client, LM85_REG_IN_MAX(i));
+		}
+
+		for (i = 0; i <= 3; ++i) {
+			data->fan_min[i] =
+			    lm85_read_value(client, LM85_REG_FAN_MIN(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->temp_min[i] =
+			    lm85_read_value(client, LM85_REG_TEMP_MIN(i));
+			data->temp_max[i] =
+			    lm85_read_value(client, LM85_REG_TEMP_MAX(i));
+		}
+
+		data->vid = lm85_read_value(client, LM85_REG_VID);
+
+		for (i = 0; i <= 2; ++i) {
+			int val ;
+			data->autofan[i].config =
+			    lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
+			val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
+			data->autofan[i].freq = val & 0x07 ;
+			data->zone[i].range = (val >> 4) & 0x0f ;
+			data->autofan[i].min_pwm =
+			    lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
+			data->zone[i].limit =
+			    lm85_read_value(client, LM85_REG_AFAN_LIMIT(i));
+			data->zone[i].critical =
+			    lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i));
+		}
+
+		i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
+		data->smooth[0] = i & 0x0f ;
+		data->syncpwm3 = i & 0x10 ;  /* Save PWM3 config */
+		data->autofan[0].min_off = i & 0x20 ;
+		data->autofan[1].min_off = i & 0x40 ;
+		data->autofan[2].min_off = i & 0x80 ;
+		i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2);
+		data->smooth[1] = (i>>4) & 0x0f ;
+		data->smooth[2] = i & 0x0f ;
+
+		i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
+		data->zone[0].hyst = (i>>4) & 0x0f ;
+		data->zone[1].hyst = i & 0x0f ;
+
+		i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
+		data->zone[2].hyst = (i>>4) & 0x0f ;
+
+		switch( ((struct lm85_data *)(client->data))->type ) {
+		case lm85b :
+		case lm85c :
+			data->tach_mode = lm85_read_value(client,
+				LM85_REG_TACH_MODE );
+			data->spinup_ctl = lm85_read_value(client,
+				LM85_REG_SPINUP_CTL );
+			break ;
+		case adt7463 :
+			for (i = 0; i <= 2; ++i) {
+			    data->oppoint[i] = lm85_read_value(client,
+				ADT7463_REG_OPPOINT(i) );
+			}
+			data->tmin_ctl = lm85_read_value(client,
+				ADT7463_REG_TMIN_CTL );
+			data->therm_limit = lm85_read_value(client,
+				ADT7463_REG_THERM_LIMIT );
+		/* FALL THROUGH */
+		case adm1027 :
+			for (i = 0; i <= 2; ++i) {
+			    data->temp_offset[i] = lm85_read_value(client,
+				ADM1027_REG_TEMP_OFFSET(i) );
+			}
+			data->tach_mode = lm85_read_value(client,
+				ADM1027_REG_CONFIG3 );
+			data->fan_ppr = lm85_read_value(client,
+				ADM1027_REG_FAN_PPR );
+			data->alarm_mask = lm85_read_value(client,
+				ADM1027_REG_INTMASK );
+			break ;
+		case emc6d100 :
+			for (i = 5; i <= 7; ++i) {
+			    data->in_min[i] =
+				lm85_read_value(client, EMC6D100_REG_IN_MIN(i));
+			    data->in_max[i] =
+				lm85_read_value(client, EMC6D100_REG_IN_MAX(i));
+			}
+			break ;
+		default : break ; /* no warnings */
+		}
+	
+		data->last_config = jiffies;
+	};  /* last_config */
+
+	data->valid = 1;
+
+	up(&data->update_lock);
+}
+
+
+/* The next 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 data
+   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.
+ */
+void lm85_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_IN0;
+
+	if (nr < 0 || nr > 4)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		int  ext = 0 ;
+		lm85_update_client(client);
+		ext = EXT_FROM_REG(data->extend_adc, nr);
+		results[0] = INS_FROM_REG(nr,data->in_min[nr]);
+		results[1] = INS_FROM_REG(nr,data->in_max[nr]);
+		results[2] = INSEXT_FROM_REG(nr,data->in[nr],ext);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_max[nr] = INS_TO_REG(nr,results[1]);
+			lm85_write_value(client, LM85_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_min[nr] = INS_TO_REG(nr,results[0]);
+			lm85_write_value(client, LM85_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_FAN1 ;
+
+	if (nr < 0 || nr > 3)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr]);
+		results[1] = FAN_FROM_REG(data->fan[nr]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->fan_min[nr] = FAN_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_FAN_MIN(nr),
+					 data->fan_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+
+void lm85_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_TEMP1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		int  ext = 0 ;
+		lm85_update_client(client);
+
+		/* +5 for offset of temp data in ext reg */
+		ext = EXT_FROM_REG(data->extend_adc, nr+5);
+
+		results[0] = TEMP_FROM_REG(data->temp_min[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_max[nr]);
+		results[2] = TEMPEXT_FROM_REG(data->temp[nr],ext);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->temp_max[nr] = TEMP_TO_REG(results[1]);
+			lm85_write_value(client, LM85_REG_TEMP_MAX(nr),
+					 data->temp_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->temp_min[nr] = TEMP_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_TEMP_MIN(nr),
+					 data->temp_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM1 ;
+	int pwm_zone ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr]);
+		pwm_zone = ZONE_FROM_REG(data->autofan[nr].config);
+		/* PWM "enabled" if not off (0) nor on (-1) */
+		results[1] = pwm_zone != 0 && pwm_zone != -1 ;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* PWM enable is read-only */
+		if (*nrels_mag > 0) {
+			data->pwm[nr] = PWM_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_PWM(nr),
+					 data->pwm[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_VID )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = vid_from_reg((data->vid)&0x3f, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void lm85_vrm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_VRM )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->vrm = results[0] ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_alarms(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_ALARMS )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void lm85_spinup_ctl(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	if( ctl_name != LM85_SYSCTL_SPINUP_CTL )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->spinup_ctl & 1) != 0 ;
+		results[1] = (data->spinup_ctl & 2) != 0 ;
+		results[2] = (data->spinup_ctl & 4) != 0 ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->spinup_ctl ;
+		if (*nrels_mag > 2) {
+			old = (old & (~4)) | (results[2]?4:0) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~2)) | (results[1]?2:0) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~1)) | (results[0]?1:0) ;
+			lm85_write_value(client, LM85_REG_SPINUP_CTL, old);
+			data->spinup_ctl = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_tach_mode(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	/* Tach Mode 1, Tach Mode 2, Tach Mode 3 & 4 */
+
+	if( ctl_name != LM85_SYSCTL_TACH_MODE )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->tach_mode & 0x03) ;
+		results[1] = (data->tach_mode & 0x0c) >> 2 ;
+		results[2] = (data->tach_mode & 0x30) >> 4 ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tach_mode ;
+		if (*nrels_mag > 2) {
+			old = (old & (~0x30)) | ((results[2]&3) << 4) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~0x0c)) | ((results[1]&3) << 2) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~0x03)) |  (results[0]&3) ;
+			lm85_write_value(client, LM85_REG_TACH_MODE, old);
+			data->tach_mode = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_tach_mode(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	/* Tach/DC 1, Tach/DC 2, Tach/DC 3, Tach/DC 4 */
+
+	if( ctl_name != ADM1027_SYSCTL_TACH_MODE )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->tach_mode & 0x10) != 0 ;
+		results[1] = (data->tach_mode & 0x20) != 0 ;
+		results[2] = (data->tach_mode & 0x40) != 0 ;
+		results[3] = (data->tach_mode & 0x80) != 0 ;
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tach_mode ;
+		if (*nrels_mag > 3) {
+			old = (old & (~0x80)) | (results[3] ? 0x80 : 0) ;
+		}
+		if (*nrels_mag > 2) {
+			old = (old & (~0x40)) | (results[2] ? 0x40 : 0) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~0x20)) | (results[1] ? 0x20 : 0) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~0x10)) | (results[0] ? 0x10 : 0) ;
+
+			/* Enable fast measurements if any TACH's are DC */
+			old = (old & (~0x08)) | ((old&0xf0) ? 0x08 : 0) ;
+
+			lm85_write_value(client, ADM1027_REG_CONFIG3, old);
+			data->tach_mode = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm_config(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM_CFG1 ;
+
+	/* Spinup, min PWM, PWM Frequency, min below limit, Invert */
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+
+		results[0] = SPINUP_FROM_REG(data->autofan[nr].config);
+		results[1] = PWM_FROM_REG(data->autofan[nr].min_pwm)*10;
+		results[2] = FREQ_FROM_REG(data->autofan[nr].freq);
+		results[3] = data->autofan[nr].min_off ? 10 : 0 ;
+		results[4] = (data->autofan[nr].config & 0x10) ? 10 : 0 ;
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		int  old_config ;
+
+		down(&data->update_lock);
+		old_config = data->autofan[nr].config ;
+		if (*nrels_mag > 4) {
+			old_config = (old_config & (~0x10)) | (results[4]?0x10:0) ;
+		}
+		if (*nrels_mag > 3) {
+			data->autofan[nr].min_off = results[3] != 0 ;
+			lm85_write_value(client, LM85_REG_AFAN_SPIKE1,
+				data->smooth[0]
+				| data->syncpwm3
+				| (data->autofan[0].min_off ? 0x20 : 0)
+				| (data->autofan[1].min_off ? 0x40 : 0)
+				| (data->autofan[2].min_off ? 0x80 : 0)
+			);
+		}
+		if (*nrels_mag > 2) {
+			data->autofan[nr].freq = FREQ_TO_REG(results[2]) ;
+			lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+			    (data->zone[nr].range << 4)
+			    | data->autofan[nr].freq
+			);
+		}
+		if (*nrels_mag > 1) {
+			data->autofan[nr].min_pwm = PWM_TO_REG((results[1]+5)/10);
+			lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr),
+					data->autofan[nr].min_pwm
+			);
+		}
+		if (*nrels_mag > 0) {
+			old_config = (old_config & (~0x07)) | SPINUP_TO_REG(results[0]) ;
+			lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), old_config);
+			data->autofan[nr].config = old_config ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_smooth(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_SMOOTH1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = SMOOTH_FROM_REG(data->smooth[nr]);
+		*nrels_mag = 1;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if( *nrels_mag > 0 ) {
+			data->smooth[nr] = SMOOTH_TO_REG(results[0]);
+		}
+		if( nr == 0 ) {
+		    lm85_write_value(client, LM85_REG_AFAN_SPIKE1,
+			data->smooth[0]
+			| data->syncpwm3
+			| (data->autofan[0].min_off ? 0x20 : 0)
+			| (data->autofan[1].min_off ? 0x40 : 0)
+			| (data->autofan[2].min_off ? 0x80 : 0)
+		    );
+		} else {
+		    lm85_write_value(client, LM85_REG_AFAN_SPIKE2,
+			(data->smooth[1] << 4) | data->smooth[2]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_zone(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_ZONE1 ;
+
+	/* Limit, Hysteresis (neg), Range, Critical */
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+
+		results[0] = TEMP_FROM_REG(data->zone[nr].limit) / 10;
+		results[1] = HYST_FROM_REG(data->zone[nr].hyst);
+		results[2] = RANGE_FROM_REG(data->zone[nr].range);
+		results[3] = TEMP_FROM_REG(data->zone[nr].critical) / 10;
+		*nrels_mag = 4;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 3) {
+			data->zone[nr].critical = TEMP_TO_REG(results[3]*10);
+			lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr),
+				data->zone[nr].critical );
+		}
+		if (*nrels_mag > 2) {
+			data->zone[nr].range = RANGE_TO_REG(results[2]);
+			lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+			    (data->zone[nr].range << 4)
+			    | data->autofan[nr].freq
+			);
+		}
+		if (*nrels_mag > 1) {
+			data->zone[nr].hyst = HYST_TO_REG(results[1]);
+			if( nr == 0 || nr == 1 ) {
+			    lm85_write_value(client, LM85_REG_AFAN_HYST1,
+				(data->zone[0].hyst << 4)
+				| data->zone[1].hyst
+			    );
+			} else {
+			    lm85_write_value(client, LM85_REG_AFAN_HYST2,
+				(data->zone[2].hyst << 4)
+			    );
+			}
+		}
+		if (*nrels_mag > 0) {
+			data->zone[nr].limit = TEMP_TO_REG(results[0]*10);
+			lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr),
+			    data->zone[nr].limit
+			);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm_zone(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM_ZONE1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = ZONE_FROM_REG(data->autofan[nr].config);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->autofan[nr].config =
+			    (data->autofan[nr].config & (~0xe0))
+			    | ZONE_TO_REG(results[0]) ;
+			lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr),
+			    data->autofan[nr].config);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_temp_offset(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - ADM1027_SYSCTL_TEMP_OFFSET1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		switch( data->type ) {
+		case adm1027 :
+		default :
+			results[0] = TEMP_FROM_REG(data->temp_offset[nr]);
+			break ;
+		case adt7463 :
+			results[0] = TEMPEXT_FROM_REG(0,data->temp_offset[nr]);
+			break ;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			switch( data->type ) {
+			case adm1027 :
+			default :
+			    data->temp_offset[nr] = TEMP_TO_REG(results[0]);
+			    break ;
+			case adt7463 :
+			    data->temp_offset[nr] = EXTTEMP_TO_REG(results[0]);
+			    break ;
+			};
+			lm85_write_value(client, ADM1027_REG_TEMP_OFFSET(nr),
+			    data->temp_offset[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_fan_ppr(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old ;
+
+	if (ctl_name != ADM1027_SYSCTL_FAN_PPR)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = PPR_FROM_REG(data->fan_ppr,0);
+		results[1] = PPR_FROM_REG(data->fan_ppr,1);
+		results[2] = PPR_FROM_REG(data->fan_ppr,2);
+		results[3] = PPR_FROM_REG(data->fan_ppr,3);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->fan_ppr ;
+		if (*nrels_mag > 3) {
+			old = (old & ~PPR_MASK(3)) | PPR_TO_REG(results[3],3);
+		};
+		if (*nrels_mag > 2) {
+			old = (old & ~PPR_MASK(2)) | PPR_TO_REG(results[2],2);
+		};
+		if (*nrels_mag > 1) {
+			old = (old & ~PPR_MASK(1)) | PPR_TO_REG(results[1],1);
+		};
+		if (*nrels_mag > 0) {
+			old = (old & ~PPR_MASK(0)) | PPR_TO_REG(results[0],0);
+			lm85_write_value(client, ADM1027_REG_FAN_PPR, old);
+			data->fan_ppr = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_alarm_mask(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != ADM1027_SYSCTL_ALARM_MASK )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = INTMASK_FROM_REG(data->alarm_mask);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->alarm_mask = INTMASK_TO_REG(results[0]);
+			lm85_write_value(client, ADM1027_REG_INTMASK,
+			    data->alarm_mask);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adt7463_tmin_ctl(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - ADT7463_SYSCTL_TMIN_CTL1 ;
+	u16 old ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		old = data->tmin_ctl ;
+		results[0] = (old & ( 0x2000 << nr )) != 0 ;
+		results[1] = (old >> (nr*3)) & 0x07  ;
+		results[2] = (old & ( 0x0400 << nr )) != 0 ;
+		results[3] = OPPOINT_FROM_REG(data->oppoint[nr]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tmin_ctl ;
+		if (*nrels_mag > 3) {
+			data->oppoint[nr] = OPPOINT_TO_REG(results[3]);
+			lm85_write_value(client, ADT7463_REG_OPPOINT(nr),
+			    data->oppoint[nr]);
+		};
+		if (*nrels_mag > 2) {
+			if( results[2] ) {
+				old |= (0x0400 << nr) ;
+			} else {
+				old &= ~(0x0400 << nr) ;
+			}
+		};
+		if (*nrels_mag > 1) {
+			old &= ~(0x07 << (nr*3)) ;
+			old |= (results[1] & 0x07) << (nr*3) ;
+		};
+		if (*nrels_mag > 0) {
+			if( results[0] ) {
+				old |= 0x2000 << nr ;
+			} else {
+				old &= ~(0x2000 << nr) ;
+			}
+			lm85_write_value(client, ADT7463_REG_TMIN_CTL, old);
+			data->tmin_ctl = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adt7463_therm_signal(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int counts ;
+
+	if (ctl_name != ADT7463_SYSCTL_THERM_SIGNAL)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		/* Don't call update_client here because
+		 *   ADT7463_REG_THERM has to be read every
+		 *   5 seconds to prevent lost counts
+		 */
+		down(&data->update_lock);
+		counts = lm85_read_value(client, ADT7463_REG_THERM) & 0xff;
+		if( data->therm_total < LONG_MAX - 256 ) {
+		    data->therm_total += counts ;
+		}
+		if( counts >= 255 ) {
+		    ++data->therm_ovfl ;
+		}
+		up(&data->update_lock);
+
+		results[0] = data->therm_limit ;
+		results[1] = data->therm_total ;
+		results[2] = data->therm_ovfl ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* therm_total and therm_ovfl are read only */
+		if (*nrels_mag > 0) {
+			data->therm_limit = SENSORS_LIMIT(results[0],0,255);
+			lm85_write_value(client, ADT7463_REG_THERM_LIMIT,
+			    data->therm_limit);
+		};
+		up(&data->update_lock);
+	}
+}
+
+
+void emc6d100_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - EMC6D100_SYSCTL_IN5 +5;
+
+	if (nr < 5 || nr > 7)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = INS_FROM_REG(nr,data->in_min[nr]);
+		results[1] = INS_FROM_REG(nr,data->in_max[nr]);
+		results[2] = INS_FROM_REG(nr,data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_max[nr] = INS_TO_REG(nr,results[1]);
+			lm85_write_value(client, EMC6D100_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_min[nr] = INS_TO_REG(nr,results[0]);
+			lm85_write_value(client, EMC6D100_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+
+static int __init sm_lm85_init(void)
+{
+	printk("lm85: Version %s (%s)\n", LM_VERSION, LM_DATE);
+	printk("lm85: See http://www.penguincomputing.com/lm_sensors for more info.\n" );
+	return i2c_add_driver(&lm85_driver);
+}
+
+static void __exit sm_lm85_exit(void)
+{
+	i2c_del_driver(&lm85_driver);
+}
+
+/* Thanks to Richard Barrington for adding the LM85 to sensors-detect.
+ * Thanks to Margit Schubert-While <margitsw@t-online.de> for help with
+ *     post 2.7.0 CVS changes
+ */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com");
+MODULE_DESCRIPTION("LM85-B, LM85-C driver");
+
+module_init(sm_lm85_init);
+module_exit(sm_lm85_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/sis5595.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/sis5595.c	(revision 3044)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/sis5595.c	(revision 3044)
@@ -0,0 +1,722 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+                        Kyösti Mälkki <kmalkki@cc.hut.fi>, 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.
+*/
+
+/* 
+   SiS southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+
+    Supports following revisions:
+	Version		PCI ID		PCI Revision
+	1		1039/0008	AF or less
+	2		1039/0008	B0 or greater
+
+   Note: these chips contain a 0008 device which is incompatible with the
+         5595. We recognize these by the presence of the listed
+         "blacklist" PCI ID and refuse to load.
+
+   NOT SUPPORTED	PCI ID		BLACKLIST PCI ID	
+	 540		0008		0540
+	 550		0008		0550
+	5513		0008		5511
+	5581		0008		5597
+	5582		0008		5597
+	5597		0008		5597
+	5598		0008		5597/5598
+	 630		0008		0630
+	 645		0008		0645
+	 730		0008		0730
+	 735		0008		0735
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* 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(sis5595);
+
+static int blacklist[] = {
+			PCI_DEVICE_ID_SI_540,
+			PCI_DEVICE_ID_SI_550,
+			PCI_DEVICE_ID_SI_630,
+			PCI_DEVICE_ID_SI_645,
+			PCI_DEVICE_ID_SI_730,
+			PCI_DEVICE_ID_SI_735,
+			PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
+						  that ID shows up in other chips so we
+						  use the 5511 ID for recognition */
+			PCI_DEVICE_ID_SI_5597,
+			PCI_DEVICE_ID_SI_5598,
+                          0 };
+
+/* Many SIS5595 constants specified below */
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* PCI Config Registers */
+#define SIS5595_REVISION_REG 0x08
+#define SIS5595_BASE_REG 0x68
+#define SIS5595_PIN_REG 0x7A
+#define SIS5595_ENABLE_REG 0x7B
+
+/* Where are the ISA address/data registers relative to the base address */
+#define SIS5595_ADDR_REG_OFFSET 5
+#define SIS5595_DATA_REG_OFFSET 6
+
+/* The SIS5595 registers */
+#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define SIS5595_REG_IN(nr) (0x20 + (nr))
+
+#define SIS5595_REG_FAN_MIN(nr) (0x3a + (nr))
+#define SIS5595_REG_FAN(nr) (0x27 + (nr))
+
+/* On the first version of the chip, the temp registers are separate.
+   On the second version,
+   TEMP pin is shared with IN4, configured in PCI register 0x7A.
+   The registers are the same as well.
+   OVER and HYST are really MAX and MIN. */
+
+#define REV2MIN	0xb0
+#define SIS5595_REG_TEMP 	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN(4) : 0x27
+#define SIS5595_REG_TEMP_OVER	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN_MAX(4) : 0x39
+#define SIS5595_REG_TEMP_HYST	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN_MIN(4) : 0x3a
+
+#define SIS5595_REG_CONFIG 0x40
+#define SIS5595_REG_ALARM1 0x41
+#define SIS5595_REG_ALARM2 0x42
+#define SIS5595_REG_FANDIV 0x47
+
+/* Conversions. 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 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm <= 0)
+		return 255;
+	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)))
+
+/* Version 1 datasheet temp=.83*reg + 52.12 */
+#define TEMP_FROM_REG(val) (((((val)>=0x80?(val)-0x100:(val))*83)+5212)/10)
+/* inverse 1.20*val - 62.77 */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?\
+				((((val)*12)-6327)/100):\
+                                ((((val)*12)-6227)/100)),0,255))
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* For the SIS5595, we need to keep some data in memory. That
+   data is pointed to by sis5595_list[NR]->data. The structure itself is
+   dynamically allocated, at the time when the new sis5595 client is
+   allocated. */
+struct sis5595_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	char maxins;		/* == 3 if temp enabled, otherwise == 4 */
+	u8 revision;		/* Reg. value */
+
+	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 */
+	u8 temp;		/* Register value */
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* 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) sis5595 */
+
+static int sis5595_attach_adapter(struct i2c_adapter *adapter);
+static int sis5595_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int sis5595_detach_client(struct i2c_client *client);
+
+static int sis5595_read_value(struct i2c_client *client, u8 register);
+static int sis5595_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void sis5595_update_client(struct i2c_client *client);
+static void sis5595_init_client(struct i2c_client *client);
+
+
+static void sis5595_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void sis5595_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void sis5595_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void sis5595_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void sis5595_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+/* 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 sis5595_driver = {
+	.name		= "SiS 5595",
+	.id		= I2C_DRIVERID_SIS5595,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= sis5595_attach_adapter,
+	.detach_client	= sis5595_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define SIS5595_SYSCTL_IN0 1000	/* Volts * 100 */
+#define SIS5595_SYSCTL_IN1 1001
+#define SIS5595_SYSCTL_IN2 1002
+#define SIS5595_SYSCTL_IN3 1003
+#define SIS5595_SYSCTL_IN4 1004
+#define SIS5595_SYSCTL_FAN1 1101	/* Rotations/min */
+#define SIS5595_SYSCTL_FAN2 1102
+#define SIS5595_SYSCTL_TEMP 1200	/* Degrees Celsius * 10 */
+#define SIS5595_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define SIS5595_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define SIS5595_ALARM_IN0 0x01
+#define SIS5595_ALARM_IN1 0x02
+#define SIS5595_ALARM_IN2 0x04
+#define SIS5595_ALARM_IN3 0x08
+#define SIS5595_ALARM_BTI 0x20
+#define SIS5595_ALARM_FAN1 0x40
+#define SIS5595_ALARM_FAN2 0x80
+#define SIS5595_ALARM_IN4  0x8000
+#define SIS5595_ALARM_TEMP 0x8000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected SIS5595. 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 sis5595_dir_table_template[] = {
+	{SIS5595_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_temp},
+	{SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan_div},
+	{SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_alarms},
+	{0}
+};
+
+/* This is called when the module is loaded */
+static int sis5595_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, sis5595_detect);
+}
+
+/* Locate SiS bridge and correct base address for SIS5595 */
+static int __init sis5595_find_sis(int *address)
+{
+	u16 val;
+	int *i;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	if (!(s_bridge =
+	      pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503,
+			     NULL)))
+		return -ENODEV;
+
+	/* Look for imposters */
+	for(i = blacklist; *i != 0; i++) {
+		if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) {
+			printk("sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i);
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val))
+		return -ENODEV;
+
+	*address = val & ~(SIS5595_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("sis5595.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	return 0;
+}
+
+int sis5595_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct sis5595_data *data;
+	int err = 0;
+	const char *type_name = "sis5595";
+	const char *client_name = "SIS5595 chip";
+	char val;
+	u16 a;
+
+	/* Make sure we are probing the ISA bus!!  */
+	if (!i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("sis5595.o: sis5595_detect called for an I2C bus adapter?!?\n");
+		return 0;
+	}
+
+	if(force_addr)
+		address = force_addr & ~(SIS5595_EXTENT - 1);
+	if (check_region(address, SIS5595_EXTENT)) {
+		printk("sis5595.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("sis5595.o: forcing ISA address 0x%04X\n", address);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
+			return -ENODEV;
+		if ((a & ~(SIS5595_EXTENT - 1)) != address) {
+			/* doesn't work for some chips? */
+			printk("sis5595.o: force address failed\n");
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
+		return -ENODEV;
+	if((val & 0x80) == 0) {
+		printk("sis5595.o: enabling sensors\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
+		                      val | 0x80))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
+			return -ENODEV;
+		if((val & 0x80) == 0) {	/* doesn't work for some chips! */
+			printk("sis5595.o: sensors enable failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &sis5595_driver;
+	new_client->flags = 0;
+
+	/* Reserve the ISA region */
+	request_region(address, SIS5595_EXTENT, type_name);
+
+	/* Check revision and pin registers to determine whether 4 or 5 voltages */
+	pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
+	/* 4 voltages, 1 temp */
+	data->maxins = 3;
+	if (data->revision >= REV2MIN) {
+		pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
+		if (!(val & 0x80))
+			/* 5 voltages, no temp */
+			data->maxins = 4;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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,
+					sis5595_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the SIS5595 chip */
+	sis5595_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, SIS5595_EXTENT);
+	kfree(data);
+	return err;
+}
+
+static int sis5595_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct sis5595_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("sis5595.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, SIS5595_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* ISA access must be locked explicitly.
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+static int sis5595_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+
+	down(&(((struct sis5595_data *) (client->data))->lock));
+	outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+	res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
+	up(&(((struct sis5595_data *) (client->data))->lock));
+	return res;
+}
+
+static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	down(&(((struct sis5595_data *) (client->data))->lock));
+	outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+	outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
+	up(&(((struct sis5595_data *) (client->data))->lock));
+	return 0;
+}
+
+/* Called when we have found a new SIS5595. */
+static void sis5595_init_client(struct i2c_client *client)
+{
+	u8 reg;
+
+	/* Start monitoring */
+	reg = i2c_smbus_read_byte_data(client, SIS5595_REG_CONFIG);
+	sis5595_write_value(client, SIS5595_REG_CONFIG, (reg|0x01)&0x7F);
+}
+
+static void sis5595_update_client(struct i2c_client *client)
+{
+	struct sis5595_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		for (i = 0; i <= data->maxins; i++) {
+			data->in[i] =
+			    sis5595_read_value(client, SIS5595_REG_IN(i));
+			data->in_min[i] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_IN_MIN(i));
+			data->in_max[i] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] =
+			    sis5595_read_value(client, SIS5595_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_FAN_MIN(i));
+		}
+		if(data->maxins == 3) {
+			data->temp =
+			    sis5595_read_value(client, SIS5595_REG_TEMP);
+			data->temp_over =
+			    sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
+			data->temp_hyst =
+			    sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
+		}
+		i = sis5595_read_value(client, SIS5595_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms =
+		    sis5595_read_value(client, SIS5595_REG_ALARM1) |
+		    (sis5595_read_value(client, SIS5595_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. */
+
+/* Return 0 for in4 and disallow writes if pin used for temp */
+void sis5595_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int nr = ctl_name - SIS5595_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if(nr <= 3 || data->maxins == 4) {
+			sis5595_update_client(client);
+			results[0] = IN_FROM_REG(data->in_min[nr]);
+			results[1] = IN_FROM_REG(data->in_max[nr]);
+			results[2] = IN_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(nr <= 3 || data->maxins == 4) {
+			if (*nrels_mag >= 1) {
+				data->in_min[nr] = IN_TO_REG(results[0]);
+				sis5595_write_value(client,
+				    SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_max[nr] = IN_TO_REG(results[1]);
+				sis5595_write_value(client,
+				    SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
+			}
+		}
+	}
+}
+
+void sis5595_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int nr = ctl_name - SIS5595_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_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]));
+			sis5595_write_value(client,
+					    SIS5595_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+/* Return 0 for temp and disallow writes if pin used for in4 */
+void sis5595_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if(data->maxins == 3) {
+			sis5595_update_client(client);
+			results[0] = TEMP_FROM_REG(data->temp_over);
+			results[1] = TEMP_FROM_REG(data->temp_hyst);
+			results[2] = TEMP_FROM_REG(data->temp);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(data->maxins == 3) {
+			if (*nrels_mag >= 1) {
+				data->temp_over = TEMP_TO_REG(results[0]);
+				sis5595_write_value(client,
+				    SIS5595_REG_TEMP_OVER, data->temp_over);
+			}
+			if (*nrels_mag >= 2) {
+				data->temp_hyst = TEMP_TO_REG(results[1]);
+				sis5595_write_value(client,
+				    SIS5595_REG_TEMP_HYST, data->temp_hyst);
+			}
+		}
+	}
+}
+
+void sis5595_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void sis5595_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_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 = sis5595_read_value(client, SIS5595_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);
+			sis5595_write_value(client, SIS5595_REG_FANDIV, old);
+		}
+	}
+}
+
+static int __init sm_sis5595_init(void)
+{
+	int addr;
+
+	printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (sis5595_find_sis(&addr)) {
+		printk("sis5595.o: SIS5595 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&sis5595_driver);
+}
+
+static void __exit sm_sis5595_exit(void)
+{
+	i2c_del_driver(&sis5595_driver);
+}
+
+
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("SiS 5595 Sensor device");
+
+module_init(sm_sis5595_init);
+module_exit(sm_sis5595_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/icspll.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/icspll.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/icspll.c	(revision 2867)
@@ -0,0 +1,286 @@
+/*
+    icspll.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 1998, 1999  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.
+*/
+
+/*
+    ** WARNING  **
+    Supports limited combinations of clock chips and busses.
+    Clock chip must be at address 0x69
+    This driver may crash your system.
+    See doc/chips/icspll for details.
+*/
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+#define ADDRESS 0x69
+static unsigned short normal_i2c[] = { ADDRESS, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(icspll);
+
+#define ICSPLL_SIZE 7
+#define MAXBLOCK_SIZE 32
+
+/* Each client has this additional data */
+struct icspll_data {
+	struct i2c_client client;
+	int sysctl_id;
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	u8 data[ICSPLL_SIZE];	/* Register values */
+	int memtype;
+};
+
+static int icspll_attach_adapter(struct i2c_adapter *adapter);
+static int icspll_detach_client(struct i2c_client *client);
+static int icspll_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+
+#if 0
+static int icspll_write_value(struct i2c_client *client, u8 reg,
+			      u16 value);
+#endif
+
+static void icspll_contents(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void icspll_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver icspll_driver = {
+	.name		= "Clock chip reader",
+	.id		= I2C_DRIVERID_ICSPLL,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= icspll_attach_adapter,
+	.detach_client	= icspll_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define ICSPLL_SYSCTL1 1000
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected ICSPLL. 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 icspll_dir_table_template[] = {
+	{ICSPLL_SYSCTL1, "reg0-6", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &icspll_contents},
+	{0}
+};
+
+/* holding place for data - block read could be as much as 32 */
+static u8 tempdata[MAXBLOCK_SIZE];
+
+static int icspll_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, icspll_detect);
+}
+
+/* This function is called by i2c_detect */
+int icspll_detect(struct i2c_adapter *adapter, int address,
+   	          unsigned short flags, int kind)
+{
+	int err, i;
+	struct i2c_client *new_client;
+	struct icspll_data *data;
+
+	err = 0;
+	/* Make sure we aren't probing the ISA bus!! */
+	if (i2c_is_isa_adapter(adapter))
+		return 0;
+
+	if (address != ADDRESS)
+		return 0;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BLOCK_DATA |
+				     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+		printk("icspll.o: Adapter does not support SMBus writes and Block reads\n");
+		goto ERROR0;
+	}
+
+	/* Allocate space for a new client structure */
+	if (!(data = kmalloc(sizeof(struct icspll_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	/* Fill the new client structure with data */
+	new_client = &data->client;
+	new_client->data = data;
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &icspll_driver;
+	new_client->flags = 0;
+	strcpy(new_client->name, "Clock chip");
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* use write-quick for detection */
+	if (i2c_smbus_write_quick(new_client, 0x00) < 0) {
+		printk("icspll.o: No device found at 0x%X\n", address);
+		goto ERROR1;
+	}
+
+	/* fill data structure so unknown registers are 0xFF */
+	data->data[0] = ICSPLL_SIZE;
+	for (i = 1; i <= ICSPLL_SIZE; i++)
+		data->data[i] = 0xFF;
+
+	/* Tell i2c-core a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR2;
+
+	/* Register a new directory entry with module sensors */
+	if ((err = i2c_register_entry(new_client, "icspll",
+					  icspll_dir_table_template,
+					  THIS_MODULE)) < 0)
+		goto ERROR3;
+	data->sysctl_id = err;
+	err = 0;
+
+      ERROR3:
+	i2c_detach_client(new_client);
+      ERROR2:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int icspll_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct icspll_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("icspll.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+
+#if 0
+/* No writes yet (PAE) */
+static int icspll_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	return i2c_smbus_write_block_data(client->adapter, client->addr,
+					  reg, value);
+}
+#endif
+
+static void icspll_update_client(struct i2c_client *client)
+{
+	struct icspll_data *data = client->data;
+	int i, len;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		len =
+		    i2c_smbus_read_block_data(client,
+					      0x00,
+					      tempdata);
+#ifdef DEBUG
+		printk("icspll.o: read returned %d values\n", len);
+#endif
+		if (len > ICSPLL_SIZE)
+			len = ICSPLL_SIZE;
+		for (i = 0; i < len; i++)
+			data->data[i] = tempdata[i];
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void icspll_contents(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	struct icspll_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		icspll_update_client(client);
+		for (i = 0; i < ICSPLL_SIZE; i++) {
+			results[i] = data->data[i];
+		}
+#ifdef DEBUG
+		printk("icspll.o: 0x%X ICSPLL Contents: ", client->addr);
+		for (i = 0; i < ICSPLL_SIZE; i++) {
+			printk(" 0x%X", data->data[i]);
+		}
+		printk(" .\n");
+#endif
+		*nrels_mag = ICSPLL_SIZE;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+
+/* No writes to the ICSPLL (yet, anyway) (PAE) */
+		printk("icspll.o: No writes to ICSPLLs supported!\n");
+	}
+}
+
+static int __init sm_icspll_init(void)
+{
+	printk("icspll.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&icspll_driver);
+}
+
+static void __exit sm_icspll_exit(void)
+{
+	i2c_del_driver(&icspll_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("ICSPLL driver");
+
+module_init(sm_icspll_init);
+module_exit(sm_icspll_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83792d.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83792d.c	(revision 4040)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83792d.c	(revision 4040)
@@ -0,0 +1,1532 @@
+/*
+    w83792d.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 2004, 2005 Winbond Electronics Corp.
+                  Chunhao Huang
+
+    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.
+
+    Note:
+    1. This driver is only for 2.4 kernel(2.4.10 or later), 2.6 kernel
+       need a different driver.
+    2. This driver is only for Winbond W83792D C version device, there
+       are also some motherboards with B version W83792D device. The
+       calculation method to in6-in7(measured value, limits) is a little
+       different between C and B version. C or B version can be identified
+       by CR[0x49h].
+    3. The function of chassis open detection need further test.
+    4. The function of vid and vrm has not been finished, because I'm NOT
+       very familiar with them. If someone can finish it, that's good,
+       then please delete this note 4.
+*/
+
+/*
+    Supports following chips:
+
+    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
+    w83792d	9	7	3	3	0x7a	0x5ca3	yes	no
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+#include "sensors_vid.h"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(w83792d);
+SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \
+		    "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static int init;
+MODULE_PARM(init, "i");
+MODULE_PARM_DESC(init, "Set to one for chip initialization");
+
+/* Enable/Disable w83792d debugging output */
+/* #define W83792D_DEBUG 1 */
+
+/* Constants specified below */
+#define W83792D_REG_GPIO_EN 0x1A
+#define W83792D_REG_CONFIG 0x40
+#define W83792D_REG_I2C_ADDR 0x48
+#define W83792D_REG_CHIPID 0x49   /* contains version ID: A/B/C */
+#define W83792D_REG_I2C_SUBADDR 0x4A
+#define W83792D_REG_PIN 0x4B
+#define W83792D_REG_IRQ 0x4C
+#define W83792D_REG_BANK 0x4E
+#define W83792D_REG_CHIPMAN 0x4F  /* contains the vendor ID */
+#define W83792D_REG_WCHIPID 0x58  /* contains the chip ID */
+#define W83792D_REG_VID_IN_B 0x17 /* ctroll in0/in1 's limit modifiability */
+
+static const u8 W83792D_REG_IN[9] = {
+	0x20,	/* Vcore A in DataSheet */
+	0x21,	/* Vcore B in DataSheet */
+	0x22,	/* VIN0 in DataSheet */
+	0x23,	/* VIN1 in DataSheet */
+	0x24,	/* VIN2 in DataSheet */
+	0x25,	/* VIN3 in DataSheet */
+	0x26,	/* 5VCC in DataSheet */
+	0xB0,	/* 5VSB in DataSheet */
+	0xB1	/* VBAT in DataSheet */
+};
+#define W83792D_REG_LOW_BITS1 0x3E  /* Low Bits I in DataSheet */
+#define W83792D_REG_LOW_BITS2 0x3F  /* Low Bits II in DataSheet */
+static const u8 W83792D_REG_IN_MAX[9] = {
+	0x2B,	/* Vcore A High Limit in DataSheet */
+	0x2D,	/* Vcore B High Limit in DataSheet */
+	0x2F,	/* VIN0 High Limit in DataSheet */
+	0x31,	/* VIN1 High Limit in DataSheet */
+	0x33,	/* VIN2 High Limit in DataSheet */
+	0x35,	/* VIN3 High Limit in DataSheet */
+	0x37,	/* 5VCC High Limit in DataSheet */
+	0xB4,	/* 5VSB High Limit in DataSheet */
+	0xB6	/* VBAT High Limit in DataSheet */
+};
+static const u8 W83792D_REG_IN_MIN[9] = {
+	0x2C,	/* Vcore A Low Limit in DataSheet */
+	0x2E,	/* Vcore B Low Limit in DataSheet */
+	0x30,	/* VIN0 Low Limit in DataSheet */
+	0x32,	/* VIN1 Low Limit in DataSheet */
+	0x34,	/* VIN2 Low Limit in DataSheet */
+	0x36,	/* VIN3 Low Limit in DataSheet */
+	0x38,	/* 5VCC Low Limit in DataSheet */
+	0xB5,	/* 5VSB Low Limit in DataSheet */
+	0xB7	/* VBAT Low Limit in DataSheet */
+};
+
+static const u8 W83792D_REG_FAN[7] = {
+	0x28,	/* FAN 1 Count in DataSheet */
+	0x29,	/* FAN 2 Count in DataSheet */
+	0x2A,	/* FAN 3 Count in DataSheet */
+	0xB8,	/* FAN 4 Count in DataSheet */
+	0xB9,	/* FAN 5 Count in DataSheet */
+	0xBA,	/* FAN 6 Count in DataSheet */
+	0xBE	/* FAN 7 Count in DataSheet */
+};
+static const u8 W83792D_REG_FAN_MIN[7] = {
+	0x3B,	/* FAN 1 Count Low Limit in DataSheet */
+	0x3C,	/* FAN 2 Count Low Limit in DataSheet */
+	0x3D,	/* FAN 3 Count Low Limit in DataSheet */
+	0xBB,	/* FAN 4 Count Low Limit in DataSheet */
+	0xBC,	/* FAN 5 Count Low Limit in DataSheet */
+	0xBD,	/* FAN 6 Count Low Limit in DataSheet */
+	0xBF	/* FAN 7 Count Low Limit in DataSheet */
+};
+#define W83792D_REG_FAN_CFG 0x84    /* FAN Configuration in DataSheet */
+static const u8 W83792D_REG_PWM[7] = {
+	0x81,	/* FAN 1 Duty Cycle, be used to control */
+	0x83,	/* FAN 2 Duty Cycle, be used to control */
+	0x94,	/* FAN 3 Duty Cycle, be used to control */
+	0xA3,	/* FAN 4 Duty Cycle, be used to control */
+	0xA4,	/* FAN 5 Duty Cycle, be used to control */
+	0xA5,	/* FAN 6 Duty Cycle, be used to control */
+	0xA6	/* FAN 7 Duty Cycle, be used to control */
+};
+
+#define W83792D_REG_TEMP1 0x27		/* TEMP 1 in DataSheet */
+#define W83792D_REG_TEMP1_OVER 0x39	/* TEMP 1 High Limit in DataSheet */
+#define W83792D_REG_TEMP1_HYST 0x3A	/* TEMP 1 Low Limit in DataSheet */
+static const u8 W83792D_REG_TEMP_ADD[2][7] = {
+	{ 0xC0,		/* TEMP 2 in DataSheet */
+	  0xC1,		/* TEMP 2(0.5 deg) in DataSheet */
+	  0xC5,		/* TEMP 2 Over High part in DataSheet */
+	  0xC6,		/* TEMP 2 Over Low part in DataSheet */
+	  0xC3,		/* TEMP 2 Thyst High part in DataSheet */
+	  0xC4,		/* TEMP 2 Thyst Low part in DataSheet */
+	  0xC2 },	/* TEMP 2 Config in DataSheet */
+	{ 0xC8,		/* TEMP 3 in DataSheet */
+	  0xC9,		/* TEMP 3(0.5 deg) in DataSheet */
+	  0xCD,		/* TEMP 3 Over High part in DataSheet */
+	  0xCE,		/* TEMP 3 Over Low part in DataSheet */
+	  0xCB,		/* TEMP 3 Thyst High part in DataSheet */
+	  0xCC,		/* TEMP 3 Thyst Low part in DataSheet */
+	  0xCA }	/* TEMP 3 Config in DataSheet */
+};
+
+static const u8 W83792D_REG_FAN_DIV[4] = {
+	0x47,	/* contains FAN2 and FAN1 Divisor */
+	0x5B,	/* contains FAN4 and FAN3 Divisor */
+	0x5C,	/* contains FAN6 and FAN5 Divisor */
+	0x9E	/* contains FAN7 Divisor. */
+};
+
+#define W83792D_REG_ALARM1 0xA9		/* realtime status register1 */
+#define W83792D_REG_ALARM2 0xAA		/* realtime status register2 */
+#define W83792D_REG_ALARM3 0xAB		/* realtime status register3 */
+#define W83792D_REG_CASE_OPEN 0x42	/* Bit 5: Case Open status bit */
+#define W83792D_REG_CASE_OPEN_CLR 0x44	/* Bit 7: Case Open CLR_CHS/Reset bit */
+
+static const u8 W83792D_REG_THERMAL[3] = {
+	0x85,	/* SmartFanI: Fan1 target value */
+	0x86,	/* SmartFanI: Fan2 target value */
+	0x96	/* SmartFanI: Fan3 target value */
+};
+
+static const u8 W83792D_REG_FAN_TOL[3] = {
+	0x87,	/* (bit3-0)SmartFan Fan1 tolerance */
+	0x87,	/* (bit7-4)SmartFan Fan2 tolerance */
+	0x97	/* (bit3-0)SmartFan Fan3 tolerance */
+};
+
+static const u8 W83792D_REG_POINTS[3][4] = {
+	{ 0x85,		/* SmartFanII: Fan1 temp point 1 */
+	  0xE3,		/* SmartFanII: Fan1 temp point 2 */
+	  0xE4,		/* SmartFanII: Fan1 temp point 3 */
+	  0xE5 },	/* SmartFanII: Fan1 temp point 4 */
+	{ 0x86,		/* SmartFanII: Fan2 temp point 1 */
+	  0xE6,		/* SmartFanII: Fan2 temp point 2 */
+	  0xE7,		/* SmartFanII: Fan2 temp point 3 */
+	  0xE8 },	/* SmartFanII: Fan2 temp point 4 */
+	{ 0x96,		/* SmartFanII: Fan3 temp point 1 */
+	  0xE9,		/* SmartFanII: Fan3 temp point 2 */
+	  0xEA,		/* SmartFanII: Fan3 temp point 3 */
+	  0xEB }	/* SmartFanII: Fan3 temp point 4 */
+};
+
+static const u8 W83792D_REG_LEVELS[3][4] = {
+	{ 0x88,		/* (bit3-0) SmartFanII: Fan1 Non-Stop */
+	  0x88,		/* (bit7-4) SmartFanII: Fan1 Level 1 */
+	  0xE0,		/* (bit7-4) SmartFanII: Fan1 Level 2 */
+	  0xE0 },	/* (bit3-0) SmartFanII: Fan1 Level 3 */
+	{ 0x89,		/* (bit3-0) SmartFanII: Fan2 Non-Stop */
+	  0x89,		/* (bit7-4) SmartFanII: Fan2 Level 1 */
+	  0xE1,		/* (bit7-4) SmartFanII: Fan2 Level 2 */
+	  0xE1 },	/* (bit3-0) SmartFanII: Fan2 Level 3 */
+	{ 0x98,		/* (bit3-0) SmartFanII: Fan3 Non-Stop */
+	  0x98,		/* (bit7-4) SmartFanII: Fan3 Level 1 */
+	  0xE2,		/* (bit7-4) SmartFanII: Fan3 Level 2 */
+	  0xE2 }	/* (bit3-0) SmartFanII: Fan3 Level 3 */
+};
+
+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), 1, 254);
+}
+
+#define IN_FROM_REG(nr,val) (((nr)<=1)?(val*2): \
+				((((nr)==6)||((nr)==7))?(val*6):(val*4)))
+#define IN_TO_REG(nr,val) (((nr)<=1)?(val/2): \
+				((((nr)==6)||((nr)==7))?(val/6):(val/4)))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT((val>=0)?((val)/10):((val)/10+256), 0, 255))
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+#define DIV_FROM_REG(val) (1 << (val))
+
+#ifdef W83792D_DEBUG
+#define ENTER()	printk(KERN_DEBUG "w83792d: ENTERING %s, line: %d\n", __FUNCTION__, __LINE__);
+#define LEAVE()	printk(KERN_DEBUG "w83792d: LEAVING %s, line: %d\n", __FUNCTION__, __LINE__);
+#else
+#define ENTER()
+#define LEAVE()
+#endif
+
+struct w83792d_data {
+	struct i2c_client client;
+	int sysctl_id;
+	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[2];	/* for secondary I2C addresses */
+
+	u8 in[9];		/* Register value */
+	u8 in_max[9];		/* Register value */
+	u8 in_min[9];		/* Register value */
+	u16 low_bits;		/* Register value */
+	u8 has_fan;		/* Bit vector */
+	u8 fan[7];		/* Register value */
+	u8 fan_min[7];		/* Register value */
+	u8 fan_cfg;		/* Configure Fan Mode */
+	u8 temp1[3];		/* Register value */
+	u8 temp_add[2][7];	/* Register value */
+	u8 fan_div[7];		/* Fan Divisor */
+	/*u8 vid;		Register encoding, combined */
+	u8 pwm[7];		/* We only consider the first 3 set of pwm,
+				   although 792 chip has 7 set of pwm. */
+	u8 pwm_flag[7];		/* indicates PWM or DC mode: 1->PWM; 0->DC */
+	/* u8 vrm;		 VRM version */
+	u32 alarms;		/* realtime status register encoding,combined */
+	u8 chassis[2];		/* [0]->Chassis status, [1]->CLR_CHS */
+	u8 thermal_cruise[3];	/* Smart FanI: Fan1,2,3 target value */
+	u8 fan_tolerance[3];	/* Fan1,2,3 tolerance(Smart Fan I/II) */
+	u8 sf2_points[3][4];	/* Smart FanII: Fan1,2,3 temperature points */
+	u8 sf2_levels[3][4];	/* Smart FanII: Fan1,2,3 duty cycle levels */
+};
+
+/* Read the w83792d register value, only use bank 0 of the 792 chip */
+static inline int
+w83792d_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Write value into the w83792d registers, only use bank 0 of the 792 chip */
+static inline int
+w83792d_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int w83792d_attach_adapter(struct i2c_adapter *adapter);
+static int w83792d_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int w83792d_detach_client(struct i2c_client *client);
+
+static void w83792d_init_client(struct i2c_client *client);
+static void w83792d_update_client(struct i2c_client *client);
+#ifdef W83792D_DEBUG
+static void w83792d_print_debug(struct w83792d_data *data);
+#endif
+static void w83792d_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void w83792d_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83792d_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83792d_temp_add(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+/*static void w83792d_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83792d_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results); */
+static void w83792d_set_fan_div(struct i2c_client *client,
+				int nr, u8 newdiv);
+static void w83792d_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83792d_alarms(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83792d_chassis(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83792d_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83792d_pwm_flag(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void w83792d_fan_cfg(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83792d_thermal_cruise(struct i2c_client *client, int operation,
+				   int ctl_name, int *nrels_mag, long *results);
+static void w83792d_fan_tolerance(struct i2c_client *client, int operation,
+				  int ctl_name, int *nrels_mag, long *results);
+static void w83792d_sf2_points(struct i2c_client *client, int operation,
+				int ctl_name, int *nrels_mag, long *results);
+static void w83792d_sf2_levels(struct i2c_client *client, int operation,
+				int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver w83792d_driver = {
+	.name		= "W83792D sensor driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83792d_attach_adapter,
+	.detach_client	= w83792d_detach_client,
+};
+
+/* The /proc/sys entries */
+/* -- SENSORS SYSCTL START -- */
+
+#define W83792D_SYSCTL_IN0 1000
+#define W83792D_SYSCTL_IN1 1001
+#define W83792D_SYSCTL_IN2 1002
+#define W83792D_SYSCTL_IN3 1003
+#define W83792D_SYSCTL_IN4 1004
+#define W83792D_SYSCTL_IN5 1005
+#define W83792D_SYSCTL_IN6 1006
+#define W83792D_SYSCTL_IN7 1007
+#define W83792D_SYSCTL_IN8 1008
+#define W83792D_SYSCTL_FAN1 1101
+#define W83792D_SYSCTL_FAN2 1102
+#define W83792D_SYSCTL_FAN3 1103
+#define W83792D_SYSCTL_FAN4 1104
+#define W83792D_SYSCTL_FAN5 1105
+#define W83792D_SYSCTL_FAN6 1106
+#define W83792D_SYSCTL_FAN7 1107
+
+#define W83792D_SYSCTL_TEMP1 1200
+#define W83792D_SYSCTL_TEMP2 1201
+#define W83792D_SYSCTL_TEMP3 1202
+/*#define W83792D_SYSCTL_VID 1300
+#define W83792D_SYSCTL_VRM 1301*/
+#define W83792D_SYSCTL_PWM_FLAG 1400
+#define W83792D_SYSCTL_PWM1 1401
+#define W83792D_SYSCTL_PWM2 1402
+#define W83792D_SYSCTL_PWM3 1403
+#define W83792D_SYSCTL_FAN_CFG 1500	/* control Fan Mode */
+#define W83792D_SYSCTL_FAN_DIV 1501
+#define W83792D_SYSCTL_CHASSIS 1502	/* control Case Open */
+#define W83792D_SYSCTL_ALARMS 1503
+
+#define W83792D_SYSCTL_THERMAL_CRUISE 1600	/* Smart Fan I: target value */
+#define W83792D_SYSCTL_FAN_TOLERANCE 1601	/* Smart Fan I/II: tolerance */
+#define W83792D_SYSCTL_SF2_POINTS_FAN1 1602	/* Smart Fan II: Fan1 points */
+#define W83792D_SYSCTL_SF2_POINTS_FAN2 1603	/* Smart Fan II: Fan2 points */
+#define W83792D_SYSCTL_SF2_POINTS_FAN3 1604	/* Smart Fan II: Fan3 points */
+#define W83792D_SYSCTL_SF2_LEVELS_FAN1 1605	/* Smart Fan II: Fan1 levels */
+#define W83792D_SYSCTL_SF2_LEVELS_FAN2 1606	/* Smart Fan II: Fan2 levels */
+#define W83792D_SYSCTL_SF2_LEVELS_FAN3 1607	/* Smart Fan II: Fan3 levels */
+
+#define W83792D_ALARM_IN0 0x0001
+#define W83792D_ALARM_IN1 0x0002
+#define W83792D_ALARM_IN2 0x0100
+#define W83792D_ALARM_IN3 0x0200
+#define W83792D_ALARM_IN4 0x0400
+#define W83792D_ALARM_IN5 0x0800
+#define W83792D_ALARM_IN6 0x1000
+#define W83792D_ALARM_IN7 0x80000
+#define W83792D_ALARM_IN8 0x100000
+#define W83792D_ALARM_TEMP1 0x0004
+#define W83792D_ALARM_TEMP2 0x0008
+#define W83792D_ALARM_TEMP3 0x0010
+#define W83792D_ALARM_FAN1 0x0020
+#define W83792D_ALARM_FAN2 0x0040
+#define W83792D_ALARM_FAN3 0x0080
+#define W83792D_ALARM_FAN4 0x200000
+#define W83792D_ALARM_FAN5 0x400000
+#define W83792D_ALARM_FAN6 0x800000
+#define W83792D_ALARM_FAN7 0x8000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for detected chip,
+   W83792D has 9 voltages 7 fans and 3 temperatures. */
+static ctl_table w83792d_dir_table_template[] =
+{
+	{W83792D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_in},
+	{W83792D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN6, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_FAN7, "fan7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan},
+	{W83792D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_temp},
+	{W83792D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_temp_add},
+	{W83792D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_temp_add},
+	/*{W83792D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_vid}, */
+	{W83792D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan_div},
+	{W83792D_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_alarms},
+	{W83792D_SYSCTL_CHASSIS, "chassis", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_chassis},
+	{W83792D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_pwm},
+	{W83792D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_pwm},
+	{W83792D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_pwm},
+	{W83792D_SYSCTL_PWM_FLAG, "pwm_flag", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_pwm_flag},
+	{W83792D_SYSCTL_FAN_CFG, "fan_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_fan_cfg},
+	/*{W83792D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83792d_vrm},  */
+	{W83792D_SYSCTL_THERMAL_CRUISE, "thermal_cruise", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_thermal_cruise},
+	{W83792D_SYSCTL_FAN_TOLERANCE, "fan_tolerance", NULL, 0, 0644,
+	 NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_fan_tolerance},
+	{W83792D_SYSCTL_SF2_POINTS_FAN1, "sf2_points_fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
+	{W83792D_SYSCTL_SF2_POINTS_FAN2, "sf2_points_fan2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
+	{W83792D_SYSCTL_SF2_POINTS_FAN3, "sf2_points_fan3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
+	{W83792D_SYSCTL_SF2_LEVELS_FAN1, "sf2_levels_fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
+	{W83792D_SYSCTL_SF2_LEVELS_FAN2, "sf2_levels_fan2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
+	{W83792D_SYSCTL_SF2_LEVELS_FAN3, "sf2_levels_fan3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
+	{0}
+};
+
+/* This function is called when:
+     * w83792d_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and w83792d_driver is still present) */
+static int w83792d_attach_adapter(struct i2c_adapter *adapter)
+{
+	int i_tmp;
+	ENTER()
+	i_tmp = i2c_detect(adapter, &addr_data, w83792d_detect);
+	LEAVE()
+	return i_tmp;
+}
+
+static int 
+w83792d_create_subclient(struct i2c_adapter *adapter,
+				struct i2c_client *new_client, int addr,
+				struct i2c_client **sub_cli) 
+{
+	int err;
+	struct i2c_client *sub_client;
+	
+	(*sub_cli) = sub_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (!(sub_client)) {
+		return -ENOMEM;
+	}
+	memset(sub_client, 0x00, sizeof(struct i2c_client));
+	sub_client->addr = 0x48 + addr;
+	sub_client->data = NULL;
+	sub_client->adapter = adapter;
+	sub_client->driver = &w83792d_driver;
+	sub_client->flags = 0;
+	strcpy(sub_client->name, "w83792d subclient");
+	if ((err = i2c_attach_client(sub_client))) {
+		printk(KERN_ERR "w83792: subclient registration "
+			"at address 0x%x failed\n", sub_client->addr);
+		kfree(sub_client);
+		return err;
+	}
+	return 0;
+}
+
+static int
+w83792d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
+		struct i2c_client *new_client)
+{
+	int i, id, err;
+	u8 val;
+	struct w83792d_data *data = new_client->data;
+
+	data->lm75[0] = NULL;
+	data->lm75[1] = NULL;
+
+	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) {
+				printk(KERN_ERR "w83792d: invalid subclient "
+					"address %d; must be 0x48-0x4f\n",
+					force_subclients[i]);
+				err = -ENODEV;
+				goto ERROR_SC_0;
+			}
+		}
+		w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR,
+					(force_subclients[2] & 0x07) |
+					((force_subclients[3] & 0x07) << 4));
+	}
+
+	val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR);
+	if (!(val & 0x08)) {
+		err = w83792d_create_subclient(adapter, new_client, val & 0x7,
+						&data->lm75[0]);
+		if (err < 0)
+			goto ERROR_SC_0;
+	}
+	if (!(val & 0x80)) {
+		if ((data->lm75[0] != NULL) &&
+			((val & 0x7) == ((val >> 4) & 0x7))) {
+			printk(KERN_ERR "w83792d: duplicate addresses 0x%x, "
+				"use force_subclient\n", data->lm75[0]->addr);
+			err = -ENODEV;
+			goto ERROR_SC_1;
+		}
+		err = w83792d_create_subclient(adapter, new_client,
+						(val >> 4) & 0x7, &data->lm75[1]);
+		if (err < 0)
+			goto ERROR_SC_1;
+	}
+
+	return 0;
+
+/* Undo inits in case of errors */
+
+ERROR_SC_1:
+	if (data->lm75[0] != NULL) {
+		i2c_detach_client(data->lm75[0]);
+		kfree(data->lm75[0]);
+	}
+ERROR_SC_0:
+	return err;
+}
+
+
+static int w83792d_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i, val1 = 0, val2 = 0;
+	struct i2c_client *new_client;
+	struct w83792d_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	ENTER()
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		LEAVE()
+		goto ERROR0;
+	}
+
+	/* 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 w83792d_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct w83792d_data), GFP_KERNEL))) {
+		printk(KERN_ERR "w83792d: Out of memory in w83792d_detect (new_client).\n");
+		err = -ENOMEM;
+		LEAVE()
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83792d_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+	if (kind < 0) {
+		if (w83792d_read_value(new_client, W83792D_REG_CONFIG)&0x80) {
+			LEAVE()
+			goto ERROR1;
+		}
+		val1 = w83792d_read_value(new_client, W83792D_REG_BANK);
+		val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN);
+#ifdef W83792D_DEBUG
+		printk(KERN_DEBUG "w83792d: val1 is: %d, val2 is: %d\n",
+						val1, val2);
+#endif
+		/* Check for Winbond ID if in bank 0 */
+		if (!(val1 & 0x07)) {  /* is Bank0 */
+			if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
+			     ((val1 & 0x80) && (val2 != 0x5c))) {
+				LEAVE()
+				goto ERROR1;
+			}
+		}
+		/* check address at 0x48. */
+		if (w83792d_read_value(new_client, W83792D_REG_I2C_ADDR)
+				!= address) {
+			LEAVE()
+			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 */
+	w83792d_write_value(new_client, W83792D_REG_BANK,
+			    (w83792d_read_value(new_client,
+						W83792D_REG_BANK) & 0x78) |
+			    0x80);
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		/* get vendor ID */
+		val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN);
+		if (val2 != 0x5c) {  /* the vendor is NOT Winbond */
+			LEAVE()
+			goto ERROR1;
+		}
+		val1 = w83792d_read_value(new_client, W83792D_REG_WCHIPID);
+		if (val1 == 0x7a) {
+			kind = w83792d;
+		} else {
+			if (kind == 0)
+				printk(KERN_WARNING "w83792d: Ignoring "
+				       "'force' parameter for unknown chip "
+				       "at adapter %d, address 0x%02x\n",
+				       i2c_adapter_id(adapter), address);
+			LEAVE()
+			goto ERROR1;
+		}
+	}
+
+	if (kind == w83792d) {
+ 		type_name = "w83792d";
+ 		client_name = "W83792D chip";
+	} else {
+		printk(KERN_ERR "w83792d: Internal error: unknown kind (%d)?!?",
+		       kind);
+		LEAVE()
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+	data->valid = 0;
+	data->has_fan = 0x07; /* at least 3 fan inputs */
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client))) {
+		LEAVE()
+		goto ERROR1;
+	}
+	
+ 	if ((err = w83792d_detect_subclients(adapter, address,
+ 			kind, new_client)))
+ 		goto ERROR2;
+
+	/* Read GPIO enable register to check if pins for fan 4,5 are used as
+	   GPIO */
+	val1 = w83792d_read_value(new_client, W83792D_REG_GPIO_EN);
+	if (!(val1 & 0x40))
+		data->has_fan |= 0x08;	/* fan 4 */
+	if (!(val1 & 0x20))
+		data->has_fan |= 0x10;	/* fan 5 */
+
+	val1 = w83792d_read_value(new_client, W83792D_REG_PIN);
+	if (val1 & 0x40)
+		data->has_fan |= 0x20;	/* fan 6 */
+	if (val1 & 0x04)
+		data->has_fan |= 0x40;	/* fan 7 */
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+				    w83792d_dir_table_template, THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR3;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the chip */
+	w83792d_init_client(new_client);
+	LEAVE()
+	return 0;
+
+ERROR3:
+	if (data->lm75[0] != NULL) {
+		i2c_detach_client(data->lm75[0]);
+		kfree(data->lm75[0]);
+	}
+	if (data->lm75[1] != NULL) {
+		i2c_detach_client(data->lm75[1]);
+		kfree(data->lm75[1]);
+	}
+ERROR2:
+	i2c_detach_client(new_client);
+ERROR1:
+	kfree(data);
+ERROR0:
+
+	LEAVE()
+	return err;
+}
+
+static int w83792d_detach_client(struct i2c_client *client)
+{
+	int err;
+	struct w83792d_data *data = client->data;
+	ENTER()
+
+	/* remove sysctl table (primary client only) */
+	if ((data))
+		i2c_deregister_entry(data->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "w83792d: Client deregistration failed, client not detached.\n");
+		LEAVE()
+		return err;
+	}
+
+	if (data) {
+		/* primary client */
+		kfree(data);
+	} else {
+		/* subclients */
+		kfree(client);
+	}
+
+	return 0;
+}
+
+/* Called when we have found a new W83792D. */
+static void w83792d_init_client(struct i2c_client *client)
+{
+	int temp2_cfg, temp3_cfg;
+	u8 vid_in_b;
+
+	ENTER()
+
+	if (init) {
+		w83792d_write_value(client, W83792D_REG_CONFIG, 0x80);
+	}
+	/* data->vrm = 90; */ /* maybe need to be modified! */
+
+	/* Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0):
+	   W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of
+	     vin0/vin1 can be modified by user;
+	   W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of
+	     vin0/vin1 auto-updated, can NOT be modified by user. */
+	vid_in_b = w83792d_read_value(client, W83792D_REG_VID_IN_B);
+	w83792d_write_value(client, W83792D_REG_VID_IN_B,
+			    vid_in_b & 0xbf);
+
+	temp2_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[0][6]);
+	temp3_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[1][6]);
+	w83792d_write_value(client, W83792D_REG_TEMP_ADD[0][6],
+			    temp2_cfg & 0xe6);
+	w83792d_write_value(client, W83792D_REG_TEMP_ADD[1][6],
+			    temp3_cfg & 0xe6);
+
+	/* Start monitoring */
+	w83792d_write_value(client, W83792D_REG_CONFIG, (w83792d_read_value(
+				client,	W83792D_REG_CONFIG) & 0xf7) | 0x01);
+	LEAVE()
+}
+
+static void w83792d_update_client(struct i2c_client *client)
+{
+	struct w83792d_data *data = client->data;
+	int i, j;
+	u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp;
+
+	down(&data->update_lock);
+
+	if (time_after(jiffies - data->last_updated, HZ * 3) ||
+	    time_before(jiffies, data->last_updated) || !data->valid) {
+		pr_debug(KERN_DEBUG "Starting device update\n");
+
+		/* Update the voltages measured value and limits */
+		for (i = 0; i < 9; i++) {
+			data->in[i] = w83792d_read_value(client,
+						W83792D_REG_IN[i]);
+			data->in_max[i] = w83792d_read_value(client,
+						W83792D_REG_IN_MAX[i]);
+			data->in_min[i] = w83792d_read_value(client,
+						W83792D_REG_IN_MIN[i]);
+		}
+		data->low_bits = w83792d_read_value(client,
+						W83792D_REG_LOW_BITS1) +
+				 (w83792d_read_value(client,
+						W83792D_REG_LOW_BITS2) << 8);
+
+		for (i = 0; i < 7; i++) {
+			/* Update the Fan measured value and limits */
+			data->fan[i] = w83792d_read_value(client,
+						W83792D_REG_FAN[i]);
+			data->fan_min[i] = w83792d_read_value(client,
+						W83792D_REG_FAN_MIN[i]);
+			/* Update the PWM/DC Value and PWM/DC flag */
+			pwm_array_tmp[i] = w83792d_read_value(client,
+						W83792D_REG_PWM[i]);
+			data->pwm[i] = pwm_array_tmp[i] & 0x0f;
+			data->pwm_flag[i] = pwm_array_tmp[i] >> 7;
+		}
+		data->fan_cfg = w83792d_read_value(client, W83792D_REG_FAN_CFG);
+
+		/* Update the Fan Divisor */
+		for (i = 0; i < 4; i++) {
+			reg_array_tmp[i] = w83792d_read_value(client, W83792D_REG_FAN_DIV[i]);
+		}
+		data->fan_div[0] = reg_array_tmp[0] & 0x07;
+		data->fan_div[1] = (reg_array_tmp[0] >> 4) & 0x07;
+		data->fan_div[2] = reg_array_tmp[1] & 0x07;
+		data->fan_div[3] = (reg_array_tmp[1] >> 4) & 0x07;
+		data->fan_div[4] = reg_array_tmp[2] & 0x07;
+		data->fan_div[5] = (reg_array_tmp[2] >> 4) & 0x07;
+		data->fan_div[6] = reg_array_tmp[3] & 0x07;
+
+		for (i = 0; i < 7; i++) {
+			if (!(data->has_fan & (1 << i)))
+				continue;
+			if (data->fan[i] == 0xff && data->fan_div[i] < 7)
+				w83792d_set_fan_div(client, i, 7);
+			else if (data->fan[i] < 0x70 && data->fan_div[i] > 0) {
+				w83792d_set_fan_div(client, i,
+							data->fan_div[i] - 1);
+			} else if (data->fan[i] > 0xf8 &&
+						data->fan_div[i] < 7) {
+				w83792d_set_fan_div(client, i,
+							data->fan_div[i] + 1);
+			}
+		}
+
+		/* Update the Temperature1 measured value and limits */
+		data->temp1[0] = w83792d_read_value(client, W83792D_REG_TEMP1);
+		data->temp1[1] = w83792d_read_value(client, W83792D_REG_TEMP1_OVER);
+		data->temp1[2] = w83792d_read_value(client, W83792D_REG_TEMP1_HYST);
+
+		/* Update the Temperature2/3 measured value and limits */
+		for (i = 0; i < 7; i++) {
+			data->temp_add[0][i] = w83792d_read_value(client,
+						W83792D_REG_TEMP_ADD[0][i]);
+			data->temp_add[1][i] = w83792d_read_value(client,
+						W83792D_REG_TEMP_ADD[1][i]);
+		}
+
+		/* Update the VID */
+		/* i = w83792d_read_value(client, W83792D_REG_FAN_DIV[0]);
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (w83792d_read_value(client, W83792D_REG_CHIPID) & 0x01)
+		    << 4;   */
+
+		/* Update the realtime status */
+		data->alarms = w83792d_read_value(client, W83792D_REG_ALARM1) +
+			(w83792d_read_value(client, W83792D_REG_ALARM2) << 8) +
+			(w83792d_read_value(client, W83792D_REG_ALARM3) << 16);
+
+		/* Update CaseOpen status and it's CLR_CHS. */
+		data->chassis[0] = (w83792d_read_value(client,
+					W83792D_REG_CASE_OPEN)
+				    >> 5) & 0x01;
+		data->chassis[1] = (w83792d_read_value(client,
+					W83792D_REG_CASE_OPEN_CLR)
+				    >> 7) & 0x01;
+
+		/* Update Thermal Cruise/Smart Fan I target value */
+		for (i = 0; i < 3; i++) {
+			data->thermal_cruise[i] =
+				w83792d_read_value(client,
+					W83792D_REG_THERMAL[i]) & 0x7f;
+		}
+
+		/* Update Smart Fan I/II tolerance */
+		reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_TOL[0]);
+		data->fan_tolerance[0] = reg_tmp & 0x0f;
+		data->fan_tolerance[1] = (reg_tmp >> 4) & 0x0f;
+		data->fan_tolerance[2] =
+		    w83792d_read_value(client, W83792D_REG_FAN_TOL[2]) & 0x0f;
+
+		/* Update Smart Fan II temperature points */
+		for (i = 0; i < 3; i++) {
+			for (j = 0; j < 4; j++) {
+				data->sf2_points[i][j] = w83792d_read_value(
+					client,W83792D_REG_POINTS[i][j]) & 0x7f;
+			}
+		}
+
+		/* Update Smart Fan II duty cycle levels */
+		for (i = 0; i < 3; i++) {
+			reg_tmp = w83792d_read_value(client,
+						W83792D_REG_LEVELS[i][0]);
+			data->sf2_levels[i][0] = reg_tmp & 0x0f;
+			data->sf2_levels[i][1] = (reg_tmp >> 4) & 0x0f;
+			reg_tmp = w83792d_read_value(client,
+						W83792D_REG_LEVELS[i][2]);
+			data->sf2_levels[i][2] = (reg_tmp >> 4) & 0x0f;
+			data->sf2_levels[i][3] = reg_tmp & 0x0f;
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+#ifdef W83792D_DEBUG
+		w83792d_print_debug(data);
+#endif
+	}
+	up(&data->update_lock);
+}
+
+/* This is a function used to debug the message. */
+#ifdef W83792D_DEBUG
+static void w83792d_print_debug(struct w83792d_data *data)
+{
+	int i=0, j=0;
+	printk(KERN_DEBUG "==========The following is the debug message...========\n");
+	printk(KERN_DEBUG "9 set of Voltages: =====>\n");
+	for (i=0; i<=8; i++) {
+		printk(KERN_DEBUG "vin[%d] is: 0x%x\n", i, data->in[i]);
+		printk(KERN_DEBUG "vin[%d] max is: 0x%x\n", i, data->in_max[i]);
+		printk(KERN_DEBUG "vin[%d] min is: 0x%x\n", i, data->in_min[i]);
+	}
+	printk(KERN_DEBUG "Low Bit1 is: 0x%x\n", data->low_bits & 0xff);
+	printk(KERN_DEBUG "Low Bit2 is: 0x%x\n", data->low_bits >> 8);
+	printk(KERN_DEBUG "7 set of Fan Counts and 3 set of Duty Cycles: =====>\n");
+	printk(KERN_DEBUG "fan_cfg is: 0x%x\n", data->fan_cfg);
+	for (i=0; i<=6; i++) {
+		printk(KERN_DEBUG "fan[%d] is: 0x%x\n", i, data->fan[i]);
+		printk(KERN_DEBUG "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
+		if (i<3) {
+			printk(KERN_DEBUG "pwm[%d]     is: 0x%x\n", i, data->pwm[i]);
+			printk(KERN_DEBUG "pwm_flag[%d] is: 0x%x\n", i, data->pwm_flag[i]);
+		}
+	}
+	printk(KERN_DEBUG "3 set of Temperatures: =====>\n");
+	printk(KERN_DEBUG "temp1 is: 0x%x\n", data->temp1[0]);
+	printk(KERN_DEBUG "temp1 high limit is: 0x%x\n", data->temp1[1]);
+	printk(KERN_DEBUG "temp1 low limit is: 0x%x\n", data->temp1[2]);
+	for (i=0; i<2; i++) {
+		for (j=0; j<7; j++) {
+			printk(KERN_DEBUG "temp_add[%d][%d] is: 0x%x\n", i, j,
+					data->temp_add[i][j]);
+		}
+	}
+	for (i=0; i<=6; i++) {
+		printk(KERN_DEBUG "fan_div[%d] is: 0x%x\n", i, data->fan_div[i]);
+	}
+	printk(KERN_DEBUG "==========End of the debug message...==================\n\n");
+}
+#endif
+
+/* 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. */
+
+/* read/write voltage meaured value and limits */
+static void w83792d_in(struct i2c_client *client, int operation, int ctl_name,
+			int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_IN0;
+
+	/* result[0]: low limit, result[1]: high limit,
+	   result[2]: measured value */
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = IN_FROM_REG(nr, data->in_min[nr]*4);
+		results[1] = IN_FROM_REG(nr, data->in_max[nr]*4);
+		/* in7 and in8 do not have low bits, but the formula still
+		   works */
+		results[2] = IN_FROM_REG(nr, ((data->in[nr] << 2) |
+						((data->low_bits >> (2 * nr))
+							& 0x03)));
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			/* Write Low limit into register. */
+			data->in_min[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[0])/4,
+							0, 255);
+			w83792d_write_value(client, W83792D_REG_IN_MIN[nr],
+							data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			/* Write High limit into register. */
+			data->in_max[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[1])/4,
+							0, 255);
+			w83792d_write_value(client, W83792D_REG_IN_MAX[nr],
+							data->in_max[nr]);
+		}
+	}
+}
+
+static void w83792d_set_fan_div(struct i2c_client *client, int nr, u8 newdiv)
+{
+	struct w83792d_data *data = client->data;
+	int min, old;
+	u8 tmp;
+
+	min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
+	old = FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]));
+	data->fan_div[nr] = newdiv;
+	tmp = w83792d_read_value(client, W83792D_REG_FAN_DIV[nr >> 1]);
+	tmp &= (nr & 1) ? 0x8f : 0xf8;
+	tmp |= (nr & 1) ? ((newdiv << 4) & 0x70) : (newdiv & 0x07);
+	w83792d_write_value(client, W83792D_REG_FAN_DIV[nr >> 1], tmp);
+	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+	data->fan[nr] = FAN_TO_REG(old, DIV_FROM_REG(data->fan_div[nr]));
+	w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
+				data->fan_min[nr]);
+}
+
+/* read/write fan meaured value and limits */
+static void w83792d_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_FAN1;
+
+	/* result[0]: low limit, result[1]: measured value */
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		if (data->has_fan & (1 << nr)) {
+			results[0] = FAN_FROM_REG(data->fan_min[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+			results[1] = FAN_FROM_REG(data->fan[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+		}
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1 && (data->has_fan & (1 << nr))) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+					    DIV_FROM_REG(data->fan_div[nr]));
+			w83792d_write_value(client,
+					     W83792D_REG_FAN_MIN[nr],
+					     data->fan_min[nr]);
+		}
+	}
+}
+
+/* read/write temperature1 meaured value and limits */
+static void w83792d_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+
+	/* result[0]: high limit, result[1]: low limit
+	   result[2]: measured value, the order is different with voltage(in) */
+	if (operation == SENSORS_PROC_REAL_INFO) {
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp1[1]);
+		results[1] = TEMP_FROM_REG(data->temp1[2]);
+		results[2] = TEMP_FROM_REG(data->temp1[0]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp1[1] = TEMP_TO_REG(results[0]);
+			w83792d_write_value(client, W83792D_REG_TEMP1_OVER,
+					    data->temp1[1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp1[2] = TEMP_TO_REG(results[1]);
+			w83792d_write_value(client, W83792D_REG_TEMP1_HYST,
+					    data->temp1[2]);
+		}
+	}
+}
+
+/* read/write temperature2,3 meaured value and limits */
+static void w83792d_temp_add(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_TEMP2;
+	int i=0, j=0;
+
+	/* result[0]: high limit, result[1]: low limit
+	   result[2]: measured value, the order is different with voltage(in) */
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (i=0; i<3; i++) {
+			j = (i==0) ? 2 : ((i==1)?0:1);
+			if ((data->temp_add[nr][i*2+1]) & 0x80) {
+				results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]) + 5;
+			} else {
+				results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]);
+			}
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_add[nr][2] = TEMP_TO_REG(results[0]);
+			w83792d_write_value(client,
+					     W83792D_REG_TEMP_ADD[nr][2],
+					     data->temp_add[nr][2]);
+			if ((results[0]%10) == 0) {
+				w83792d_write_value(client,
+					W83792D_REG_TEMP_ADD[nr][3], 0x00);
+			} else { /* consider the 0.5 degree */
+				w83792d_write_value(client,
+					W83792D_REG_TEMP_ADD[nr][3], 0x80);
+			}
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_add[nr][4] = TEMP_TO_REG(results[1]);
+			w83792d_write_value(client,
+					     W83792D_REG_TEMP_ADD[nr][4],
+					     data->temp_add[nr][4]);
+			if ((results[1]%10) == 0) {
+				w83792d_write_value(client,
+					W83792D_REG_TEMP_ADD[nr][5], 0x00);
+			} else { /* consider the 0.5 degree */
+				w83792d_write_value(client,
+					W83792D_REG_TEMP_ADD[nr][5], 0x80);
+			}
+		}
+	}
+}
+
+/*
+void w83792d_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void w83792d_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->vrm = results[0];
+		}
+	}
+} */
+
+/* Read/Write Fan Divisor */
+static void w83792d_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int i = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (i=0; i<7; i++) {
+			results[i] = DIV_FROM_REG(data->fan_div[i]);
+		}
+		*nrels_mag = 7;
+	}
+}
+
+
+/* Under Smart Fan I mode: read/write the Fan1/2/3 target temperature */
+static void w83792d_thermal_cruise(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int i=0;
+	u8 target_tmp=0, target_mask=0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (i=0; i<3; i++) {
+			results[i] = data->thermal_cruise[i];
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i=0; i<3; i++) {
+			if (*nrels_mag < (i+1)) {
+				return;
+			}
+			target_tmp = results[i];
+			target_tmp = target_tmp & 0x7f;
+			target_mask = w83792d_read_value(client,
+						W83792D_REG_THERMAL[i]) & 0x80;
+			data->thermal_cruise[i] = SENSORS_LIMIT(target_tmp, 0, 255);
+			w83792d_write_value(client, W83792D_REG_THERMAL[i],
+					     (data->thermal_cruise[i])|target_mask);
+		}
+	}
+}
+
+/* The tolerance of fan1/fan2/fan3, when using Thermal Cruise(Smart Fan I)
+   or Smart Fan II mode. */
+static void w83792d_fan_tolerance(struct i2c_client *client, int operation,
+				 int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int i=0;
+	u8 tol_tmp, tol_mask;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (i=0; i<3; i++) {
+			results[i] = data->fan_tolerance[i];
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i=0; i<3; i++) {
+			if (*nrels_mag < (i+1)) {
+				return;
+			}
+			tol_mask = w83792d_read_value(client,
+				W83792D_REG_FAN_TOL[i]) & ((i==1)?0x0f:0xf0);
+			tol_tmp = SENSORS_LIMIT(results[i], 0, 15);
+			tol_tmp &= 0x0f;
+			data->fan_tolerance[i] = tol_tmp;
+			if (i==1) {
+				tol_tmp <<= 4;
+			}
+			w83792d_write_value(client, W83792D_REG_FAN_TOL[i],
+					     tol_mask|tol_tmp);
+		}
+	}
+}
+
+/* Under Smart Fan II mode: read/write the Fan1/2/3 temperature points */
+static void w83792d_sf2_points(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_SF2_POINTS_FAN1;
+	int j=0;
+	u8 mask_tmp = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (j=0; j<4; j++) {
+			results[j] = data->sf2_points[nr][j];
+		}
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (j=0; j<4; j++) {
+			if (*nrels_mag < (j+1)) {
+				return;
+			}
+			data->sf2_points[nr][j] = SENSORS_LIMIT(results[j],
+							0, 127);
+			mask_tmp = w83792d_read_value(client,
+					W83792D_REG_POINTS[nr][j]) & 0x80;
+			w83792d_write_value(client, W83792D_REG_POINTS[nr][j],
+					mask_tmp|data->sf2_points[nr][j]);
+		}
+	}
+}
+
+/* Smart Fan II Duty Cycle1/2/3 of Fan1/2/3.
+   Notice that: The Non-Stop can NOT be modified by user,
+   because it is related with some physical characters,
+   usually set by BIOS. User's modification to it may lead to
+   Fan's stop, then bring danger. */
+static void w83792d_sf2_levels(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_SF2_LEVELS_FAN1;
+	int j = 0;
+	u8 mask_tmp = 0, level_tmp = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (j=0; j<4; j++) {
+			results[j] = (data->sf2_levels[nr][j] * 100) / 15;
+		}
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (j=1; j<4; j++) {  /* start with 1: need ignore Non-Stop */
+			if (*nrels_mag < j) {
+				return;
+			}
+			data->sf2_levels[nr][j] =
+				SENSORS_LIMIT((results[j]*15)/100, 0, 15);
+			mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[nr][j])
+						& ((j==3) ? 0xf0 : 0x0f);
+			if (j==3) {
+				level_tmp = data->sf2_levels[nr][j];
+			} else {
+				level_tmp = data->sf2_levels[nr][j] << 4;
+			}
+			w83792d_write_value(client, W83792D_REG_LEVELS[nr][j],
+						level_tmp | mask_tmp);
+		}
+	}
+}
+
+/* get reatime status of all sensors items: voltage, temp, fan */
+static void w83792d_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+/* Read/Write Chassis status and Reset Chassis. */
+static void w83792d_chassis(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	u8 temp1 = 0, temp2 = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = data->chassis[0];
+		results[1] = data->chassis[1];
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		data->chassis[1] = SENSORS_LIMIT(results[1], 0 ,1);
+		temp1 = ((data->chassis[1]) << 7) & 0x80;
+		temp2 = w83792d_read_value(client,
+				W83792D_REG_CASE_OPEN_CLR) & 0x7f;
+		w83792d_write_value(client,
+				W83792D_REG_CASE_OPEN_CLR,
+				temp1|temp2);
+	}
+}
+
+/* Read/Write PWM/DC value of Fan1,Fan2,Fan3, which controls the
+   Fan Duty Cycle */
+static void w83792d_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int nr = ctl_name - W83792D_SYSCTL_PWM1;
+	u8 pwm_mask;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = data->pwm[nr] << 4;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		data->pwm[nr] = SENSORS_LIMIT(results[0], 0, 255) >> 4;
+		pwm_mask = w83792d_read_value(client,W83792D_REG_PWM[nr]) & 0xf0;
+		w83792d_write_value(client,W83792D_REG_PWM[nr],pwm_mask|data->pwm[nr]);
+	}
+}
+
+/* Read/Write PWM/DC mode for Fan1,Fan2,Fan3:
+   1->PWM mode, 0->DC mode */
+static void w83792d_pwm_flag(struct i2c_client *client, int operation, int ctl_name,
+		      int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	int i = 0;
+	u8 pwm_flag_mask;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		for (i=0; i<3; i++) {
+			results[i] = data->pwm_flag[i];
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i=0; i<3; i++) {
+			if (*nrels_mag < (i+1)) {
+				return;
+			}
+			data->pwm_flag[i] = SENSORS_LIMIT(results[i], 0, 1);
+			pwm_flag_mask = w83792d_read_value(client,
+						W83792D_REG_PWM[i]) & 0x7f;
+			w83792d_write_value(client, W83792D_REG_PWM[i],
+					((data->pwm_flag[i])<<7)|pwm_flag_mask);
+		}
+	}
+}
+
+/* Read/Write Fan mode into:PWM/DC, Thermal Cruise(SmartFanI), SmartFanII
+   0->PWM/DC mode, 1->Thermal Cruise mode, 2/3->SmartFanII mode */
+static void w83792d_fan_cfg(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83792d_data *data = client->data;
+	u8 temp_cfg1, temp_cfg2, temp_cfg3, temp_cfg4;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83792d_update_client(client);
+		results[0] = (data->fan_cfg) & 0x03;      /* Fan1's Mode */
+		results[1] = ((data->fan_cfg)>>2) & 0x03; /* Fan2's Mode */
+		results[2] = ((data->fan_cfg)>>4) & 0x03; /* Fan3's Mode */
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag < 3) {
+			return;
+		}
+		temp_cfg1 = SENSORS_LIMIT(results[0], 0, 3);
+		temp_cfg2 = SENSORS_LIMIT(results[1], 0, 3) << 2;
+		temp_cfg3 = SENSORS_LIMIT(results[2], 0, 3) << 4;
+		temp_cfg4 = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0;
+		data->fan_cfg = ((temp_cfg4|temp_cfg3)|temp_cfg2)|temp_cfg1;
+		w83792d_write_value(client,W83792D_REG_FAN_CFG,data->fan_cfg);
+	}
+}
+
+static int __init sm_w83792d_init(void)
+{
+	ENTER()
+
+	printk(KERN_INFO "w83792d version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	LEAVE()
+	return i2c_add_driver(&w83792d_driver);
+}
+
+static void __exit sm_w83792d_exit(void)
+{
+	ENTER()
+
+	i2c_del_driver(&w83792d_driver);
+
+	LEAVE()
+}
+
+
+MODULE_AUTHOR("Chunhao Huang @ Winbond");
+MODULE_DESCRIPTION("W83792AD/D driver for linux-2.4");
+MODULE_LICENSE("GPL");
+
+module_init(sm_w83792d_init);
+module_exit(sm_w83792d_exit);
+
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/eeprom.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/eeprom.c	(revision 3156)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/eeprom.c	(revision 3156)
@@ -0,0 +1,370 @@
+/*
+    eeprom.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    2003-08-18  Jean Delvare <khali@linux-fr.org>
+    Divide the eeprom in 2-row (arbitrary) slices. This significantly
+    speeds sensors up, as well as various scripts using the eeprom
+    module.
+
+    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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <linux/sched.h> /* for capable() */
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x50, 0x57, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(eeprom);
+
+
+/* possible natures */
+#define NATURE_UNKNOWN 0
+#define NATURE_VAIO 1
+
+/* Size of EEPROM in bytes */
+#define EEPROM_SIZE 256
+
+/* Each client has this additional data */
+struct eeprom_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	u8 valid;		/* bitfield, bit!=0 if slice is valid */
+	unsigned long last_updated[8];	/* In jiffies, 8 slices */
+
+	u8 data[EEPROM_SIZE];	/* Register values */
+	u8 nature;
+};
+
+
+static int eeprom_attach_adapter(struct i2c_adapter *adapter);
+static int eeprom_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int eeprom_detach_client(struct i2c_client *client);
+
+#if 0
+static int eeprom_write_value(struct i2c_client *client, u8 reg,
+			      u8 value);
+#endif
+
+static void eeprom_contents(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void eeprom_update_client(struct i2c_client *client, u8 slice);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver eeprom_driver = {
+	.name		= "EEPROM READER",
+	.id		= I2C_DRIVERID_EEPROM,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= eeprom_attach_adapter,
+	.detach_client	= eeprom_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define EEPROM_SYSCTL1 1000
+#define EEPROM_SYSCTL2 1001
+#define EEPROM_SYSCTL3 1002
+#define EEPROM_SYSCTL4 1003
+#define EEPROM_SYSCTL5 1004
+#define EEPROM_SYSCTL6 1005
+#define EEPROM_SYSCTL7 1006
+#define EEPROM_SYSCTL8 1007
+#define EEPROM_SYSCTL9 1008
+#define EEPROM_SYSCTL10 1009
+#define EEPROM_SYSCTL11 1010
+#define EEPROM_SYSCTL12 1011
+#define EEPROM_SYSCTL13 1012
+#define EEPROM_SYSCTL14 1013
+#define EEPROM_SYSCTL15 1014
+#define EEPROM_SYSCTL16 1015
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected EEPROM. 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 eeprom_dir_table_template[] = {
+	{EEPROM_SYSCTL1, "00", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL2, "10", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL3, "20", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL4, "30", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL5, "40", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL6, "50", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL7, "60", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL8, "70", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL9, "80", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL10, "90", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL11, "a0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL12, "b0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL13, "c0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL14, "d0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL15, "e0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL16, "f0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{0}
+};
+
+static int eeprom_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, eeprom_detect);
+}
+
+/* This function is called by i2c_detect */
+static int eeprom_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct eeprom_data *data;
+	int err = 0;
+	const char *type_name;
+
+	/* There are three ways we can read the EEPROM data:
+	   (1) I2C block reads (faster, but unsupported by most adapters)
+	   (2) Consecutive byte reads (100% overhead)
+	   (3) Regular byte data reads (200% overhead)
+	   The third method is not implemented by this driver because all
+	   known adapters support at least the second. */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
+					    | I2C_FUNC_SMBUS_BYTE))
+		goto ERROR0;
+
+	if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	memset(data->data, 0xff, EEPROM_SIZE);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &eeprom_driver;
+	new_client->flags = 0;
+
+	/* prevent 24RF08 corruption */
+	i2c_smbus_write_quick(new_client, 0);
+
+	type_name = "eeprom";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, "EEPROM chip");
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+	data->nature = NATURE_UNKNOWN;
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Detect the Vaio nature of EEPROMs.
+	   We use the "PCG-" prefix as the signature. */
+	if (address == 0x57) {
+		if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P'
+		 && i2c_smbus_read_byte(new_client) == 'C'
+		 && i2c_smbus_read_byte(new_client) == 'G'
+		 && i2c_smbus_read_byte(new_client) == '-') {
+			printk(KERN_INFO "Vaio EEPROM detected, "
+			       "enabling password protection\n");
+			data->nature = NATURE_VAIO;
+		}
+	}
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					eeprom_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int eeprom_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct eeprom_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("eeprom.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+#if 0
+/* No writes yet (PAE) */
+static int eeprom_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+#endif
+
+static void eeprom_update_client(struct i2c_client *client, u8 slice)
+{
+	struct eeprom_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if (!(data->valid & (1 << slice))
+	 || (jiffies - data->last_updated[slice] > 300 * HZ)
+	 || (jiffies < data->last_updated[slice])) {
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "eeprom.o: Starting update, slice %u\n", slice);
+#endif
+
+		if (i2c_check_functionality(client->adapter,
+		                            I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		{
+			for (i = slice << 5; i < (slice + 1) << 5;
+			                            i += I2C_SMBUS_I2C_BLOCK_MAX)
+				if (i2c_smbus_read_i2c_block_data(client,
+				                           i, data->data + i)
+				                    != I2C_SMBUS_I2C_BLOCK_MAX) {
+					printk(KERN_WARNING "eeprom.o: block read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+		} else {
+			if (i2c_smbus_write_byte(client, slice << 5)) {
+				printk(KERN_WARNING "eeprom.o: read start fail at 0x%.2x!\n", slice << 5);
+				goto DONE;
+			}
+			for (i = slice << 5; i < (slice + 1) << 5; i++) {
+				j = i2c_smbus_read_byte(client);
+				if (j < 0) {
+					printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+				data->data[i] = (u8) j;
+			}
+		}
+		data->last_updated[slice] = jiffies;
+		data->valid |= (1 << slice);
+	}
+DONE:
+	up(&data->update_lock);
+}
+
+
+void eeprom_contents(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	int nr = ctl_name - EEPROM_SYSCTL1;
+	struct eeprom_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		eeprom_update_client(client, nr >> 1);
+		/* Hide Vaio security settings to regular users */
+		if (nr == 0 && data->nature == NATURE_VAIO
+		 && !capable(CAP_SYS_ADMIN))
+			for (i = 0; i < 16; i++)
+				results[i] = 0;
+		else
+			for (i = 0; i < 16; i++)
+				results[i] = data->data[i + nr * 16];
+#ifdef DEBUG
+		printk(KERN_DEBUG "eeprom.o: 0x%X EEPROM contents (row %d):",
+		       client->addr, nr + 1);
+		if (nr == 0 && data->nature == NATURE_VAIO)
+		 	printk(" <hidden for security reasons>\n");
+		else {
+			for (i = 0; i < 16; i++)
+				printk(" 0x%02X", data->data[i + nr * 16]);
+			printk("\n");
+		}
+#endif
+		*nrels_mag = 16;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+
+/* No writes to the EEPROM (yet, anyway) (PAE) */
+		printk("eeprom.o: No writes to EEPROMs supported!\n");
+	}
+}
+
+static int __init sm_eeprom_init(void)
+{
+	printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&eeprom_driver);
+}
+
+static void __exit sm_eeprom_exit(void)
+{
+	i2c_del_driver(&eeprom_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("EEPROM driver");
+
+module_init(sm_eeprom_init);
+module_exit(sm_eeprom_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm87.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm87.c	(revision 3190)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/lm87.c	(revision 3190)
@@ -0,0 +1,982 @@
+/*
+    LM87.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>
+                        Philip Edelbrock <phil@netroedge.com>
+			Stephen Rousset <stephen.rousset@rocketlogix.com>
+			Dan Eaton <dan.eaton@rocketlogix.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.
+*/
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include "version.h"
+#include "sensors_vid.h"
+
+/* Chip configuration settings.  These should be set to reflect the
+HARDWARE configuration of your chip.  By default (read: when all of
+these are left commented out), this driver assumes that the
+configuration is the same as National's defaults for the Channel Mode
+register.
+
+Set to '1' the appropriate defines, as nessesary:
+
+ - External temp sensors 2 (possible second CPU temp)
+   This will disable the 2.5V and Vccp2 readings.
+   Ironically, National decided that you can read the
+   temperature of a second CPU or it's core voltage,
+   but not both!  Comment out if FAULT is reported.  */
+
+/* #define LM87_EXT2 1 */
+
+/* Aux analog input. When enabled, the Fan 1 reading 
+   will be disabled */
+
+/* #define LM87_AIN1 1 */
+
+/* Aux analog input 2. When enabled, the Fan 2 reading 
+   will be disabled */
+
+/* #define LM87_AIN2 1 */
+
+/* Internal Vcc is 5V instead of 3.3V */
+
+/* #define LM87_5V_VCC 1 */
+
+/* That's the end of the hardware config defines.  I would have made
+   them insmod params, but it would be too much work. ;') */
+
+
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm87);
+
+/* The following is the calculation for the register offset
+ * for the monitored items minimum and maximum locations.
+ */
+#define LM87_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define LM87_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define LM87_REG_IN(nr) (0x20 + (nr))
+
+/* Initial limits */
+
+/*
+ * LM87 register definition
+ * 
+ */
+
+      /* The LM87 registers */
+#define LM87_INT_TEMP_HI_LIMIT_LOCKABLE  0x13
+#define LM87_EXT_TEMP_HI_LIMIT_LOCKABLE  0x14
+#define LM87_REG_TEST                    0x15
+#define LM87_REG_CHANNEL_MODE            0x16
+#define LM87_REG_INT_TEMP_HI_LIMIT       0x17
+#define LM87_REG_EXT_TEMP_HI_LIMIT       0x18
+#define LM87_REG_ANALOG_OUT              0x19
+
+      /* These are all read-only */
+#define LM87_REG_2_5V_EXT_TEMP_2         0x20
+#define LM87_REG_VCCP1                   0x21
+#define LM87_REG_3_3V                    0x22  
+#define LM87_REG_5V                      0x23
+#define LM87_REG_12V                     0x24
+#define LM87_REG_VCCP2                   0x25
+#define LM87_REG_EXT_TEMP_1              0x26
+#define LM87_REG_INT_TEMP                0x27  /* LM87 temp. */
+#define LM87_REG_FAN1_AIN1               0x28
+#define LM87_REG_FAN2_AIN2               0x29
+
+/* These are read/write */
+#define LM87_REG_AIN1_LOW                0x1A
+#define LM87_REG_AIN2_LOW                0x1B
+#define LM87_REG_2_5V_EXT_TEMP_2_HIGH    0x2B  
+#define LM87_REG_2_5V_EXT_TEMP_2_LOW     0x2C  
+#define LM87_REG_VCCP1_HIGH              0x2D  
+#define LM87_REG_VCCP1_LOW               0x2E  
+#define LM87_REG_3_3V_HIGH               0x2F
+#define LM87_REG_3_3V_LOW                0x30
+#define LM87_REG_5V_HIGH                 0x31
+#define LM87_REG_5V_LOW                  0x32
+#define LM87_REG_12V_HIGH                0x33
+#define LM87_REG_12V_LOW                 0x34
+#define LM87_REG_VCCP2_HIGH              0x35
+#define LM87_REG_VCCP2_LOW               0x36
+#define LM87_REG_EXT_TEMP_1_HIGH         0x37    
+#define LM87_REG_EXT_TEMP_1_LOW          0x38  
+#define LM87_REG_INT_TEMP_HIGH           0x39  
+#define LM87_REG_INT_TEMP_LOW            0x3A  
+#define LM87_REG_FAN1_AIN1_LIMIT         0x3B
+#define LM87_REG_FAN2_AIN2_LIMIT         0x3C
+#define LM87_REG_COMPANY_ID              0x3E 
+#define LM87_REG_DIE_REV                 0x3F
+
+#define LM87_REG_CONFIG                  0x40
+#define LM87_REG_INT1_STAT               0x41
+#define LM87_REG_INT2_STAT               0x42
+#define LM87_REG_INT1_MASK               0x43
+#define LM87_REG_INT2_MASK               0x44
+#define LM87_REG_CHASSIS_CLEAR           0x46
+#define LM87_REG_VID_FAN_DIV             0x47
+#define LM87_REG_VID4                    0x49
+#define LM87_REG_CONFIG_2                0x4A
+#define LM87_REG_INTRPT_STATUS_1_MIRROR  0x4C
+#define LM87_REG_INTRPT_STATUS_2_MIRROR  0x4D
+#define LM87_REG_SMBALERT_NUM_ENABLE     0x80
+
+
+
+/* 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. */
+
+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/((div)*(val)))
+
+#define TEMP_FROM_REG(val) ((val)*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(val)-5:\
+                                              (val)+5)/10,-128,127)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+/* For each registered LM87, we need to keep some data in memory. The
+   structure is dynamically allocated whenever a new LM87 client is
+   found. */
+struct lm87_data {
+	struct i2c_client client;
+	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  in[6];		/* Scaled Register value */
+	u8  in_max[6];		/* Scaled Register value */
+	u8  in_min[6];		/* Scaled Register value */
+	u8  ain1;		/* Register value */
+	u8  ain1_min;		/* Register value */
+	u8  ain1_max;		/* Register value */
+	u8  ain2;		/* Register value */
+	u8  ain2_min;		/* Register value */
+	u8  ain2_max;		/* Register value */
+	u8  fan;		/* Register value */
+	u8  fan_min;		/* Register value */
+	u8  fan_div;		/* Register encoding, shifted right */
+	u8  fan2;		/* Register value */
+	u8  fan2_min;		/* Register value */
+	u8  fan2_div;		/* Register encoding, shifted right */
+	s8  ext2_temp;		/* Register value */
+	s8  ext_temp;		/* Register value */
+	s8  int_temp;		/* Register value */
+	u8  ext_temp_max;       /* Register value */
+	u8  ext_temp_min;       /* Register value */
+	u8  ext2_temp_max; 	/* Register value */
+	u8  ext2_temp_min;	/* Register value */
+	u8  int_temp_max;       /* Register value */
+	u8  int_temp_min;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+	u8  analog_out;		/* Register value */
+	u8  vid;		/* Register value combined */
+	u8  vrm;		/* VRM version * 10 */
+};
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter);
+static int lm87_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int lm87_detach_client(struct i2c_client *client);
+
+static int lm87_read_value(struct i2c_client *client, u8 register);
+static int lm87_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void lm87_update_client(struct i2c_client *client);
+static void lm87_init_client(struct i2c_client *client);
+
+
+static void lm87_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#if defined (LM87_AIN1) || defined (LM87_AIN2)
+static void lm87_ain(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#endif
+static void lm87_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm87_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void lm87_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void lm87_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void lm87_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void lm87_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm87_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver LM87_driver = {
+	.name		= "LM87 sensor driver",
+	.id		= I2C_DRIVERID_LM87,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm87_attach_adapter,
+	.detach_client	= lm87_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define LM87_SYSCTL_IN0        1000 /* Volts * 100 */
+#define LM87_SYSCTL_IN1        1001
+#define LM87_SYSCTL_IN2        1002
+#define LM87_SYSCTL_IN3        1003
+#define LM87_SYSCTL_IN4        1004
+#define LM87_SYSCTL_IN5        1005
+#define LM87_SYSCTL_AIN1       1006
+#define LM87_SYSCTL_AIN2       1007
+#define LM87_SYSCTL_FAN1       1102
+#define LM87_SYSCTL_FAN2       1103
+#define LM87_SYSCTL_TEMP1      1250 /* Degrees Celsius * 10 */
+#define LM87_SYSCTL_TEMP2      1251 /* Degrees Celsius * 10 */
+#define LM87_SYSCTL_TEMP3      1252 /* Degrees Celsius * 10 */
+#define LM87_SYSCTL_FAN_DIV    2000 /* 1, 2, 4 or 8 */
+#define LM87_SYSCTL_ALARMS     2001 /* bitvector */
+#define LM87_SYSCTL_ANALOG_OUT 2002
+#define LM87_SYSCTL_VID        2003
+#define LM87_SYSCTL_VRM        2004
+
+#define LM87_ALARM_IN0          0x0001
+#define LM87_ALARM_IN1          0x0002
+#define LM87_ALARM_IN2          0x0004
+#define LM87_ALARM_IN3          0x0008
+#define LM87_ALARM_TEMP1        0x0010
+#define LM87_ALARM_TEMP2        0x0020
+#define LM87_ALARM_TEMP3        0x0020 /* same?? */
+#define LM87_ALARM_FAN1         0x0040
+#define LM87_ALARM_FAN2         0x0080
+#define LM87_ALARM_IN4          0x0100
+#define LM87_ALARM_IN5          0x0200
+#define LM87_ALARM_RESERVED1    0x0400
+#define LM87_ALARM_RESERVED2    0x0800
+#define LM87_ALARM_CHAS         0x1000
+#define LM87_ALARM_THERM_SIG    0x2000
+#define LM87_ALARM_TEMP2_FAULT  0x4000
+#define LM87_ALARM_TEMP3_FAULT 0x08000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected LM87. 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 LM87_dir_table_template[] = {
+#ifdef LM87_AIN1
+	{LM87_SYSCTL_AIN1, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_ain},
+#endif
+#ifdef LM87_AIN2
+	{LM87_SYSCTL_AIN2, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_ain},
+#endif
+#ifndef LM87_EXT2
+	{LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+#endif
+	{LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+#ifndef LM87_AIN1
+	{LM87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan},
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan_div},
+#define LM87_FANDIV_FLAG
+#endif
+#ifndef LM87_AIN2
+	{LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan},
+#ifndef LM87_FANDIV_FLAG
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan_div},
+#endif /* LM87_FANDIV_FLAG */
+#endif /* LM87_AIN2 */
+#ifdef LM87_EXT2
+        {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+#endif
+	{LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+	{LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+	{LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_alarms},
+	{LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_analog_out},
+	{LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_vid},
+	{LM87_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm87_vrm},
+	{0}
+};
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter)
+{
+	int error;
+	struct i2c_client_address_data  lm87_client_data;
+
+	lm87_client_data.normal_i2c       = addr_data.normal_i2c;
+	lm87_client_data.normal_i2c_range = addr_data.normal_i2c_range;
+	lm87_client_data.probe            = addr_data.probe;
+	lm87_client_data.probe_range      = addr_data.probe_range;
+	lm87_client_data.ignore           = addr_data.ignore;
+	lm87_client_data.ignore_range     = addr_data.ignore_range;
+	lm87_client_data.force            = addr_data.forces->force;
+
+	error = i2c_probe(adapter, &lm87_client_data, lm87_detect);
+	i2c_detect(adapter, &addr_data, lm87_detect);
+
+        return error;
+}
+
+static int lm87_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct lm87_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* 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 LM87_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &LM87_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (((lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
+		     != 0x00) ||
+		    (lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02))
+	       goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put into the global list */
+        type_name = "lm87";
+        client_name = "LM87 chip";
+	strcpy(new_client->name, client_name);
+	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;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					LM87_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the LM87 chip */
+	lm87_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int lm87_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm87_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "lm87.o: Client deregistration failed, "
+		       "client not detached\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+#define MAX_RETRIES 5
+
+static int lm87_read_value(struct i2c_client *client, u8 reg)
+{
+	int value, i;
+
+	/* Retry in case of read errors */
+	for (i = 1; i <= MAX_RETRIES; i++) {
+		if ((value = i2c_smbus_read_byte_data(client, reg)) >= 0)
+			return value;
+
+		printk(KERN_WARNING "lm87.o: Read byte data failed, "
+		       "address 0x%02x\n", reg);
+		mdelay(i + 3);
+	}
+
+	/* <TODO> what to return in case of error? */
+	printk(KERN_ERR "lm87.o: All read byte retries failed!!\n");
+	return 0;
+}
+
+static int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM87. */
+static void lm87_init_client(struct i2c_client *client)
+{
+	struct lm87_data *data = client->data;
+	u8 reg;
+
+        /* Setup Channel Mode register for configuration of monitoring 
+	 * Default is 00000000b
+	 * 	bit 0 - Configures Fan 1/AIN 1 input (1 = AIN)
+	 * 	bit 1 - Configures Fan 2/AIN 2 input (1 = AIN)
+	 * 	bit 2 - Configures 2.5V&Vccp2/D2 input (1 = 2nd Therm.) 
+	 * 	bit 3 - Configures Vcc for 5V/3.3V reading (0 = 3.3V)
+	 * 	bit 4 - Configures IRQ0 Enable if = 1
+	 * 	bit 5 - Configures IRQ1 Enable if = 1
+	 * 	bit 6 - Configures IRQ2 Enable if = 1
+	 * 	bit 7 - Configures VID/IRQ input as interrupts if = 1
+	 */
+
+	/* Preserve 4 MSB */
+	reg = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+/* I know, not clean, but it works. :'p */
+	lm87_write_value(client, LM87_REG_CHANNEL_MODE, (reg & 0xf0) |
+#ifdef LM87_AIN1
+ 0x01
+#else
+0
+#endif
+ | 
+#ifdef LM87_AIN2
+ 0x02
+#else
+0
+#endif
+ |
+#ifdef LM87_EXT2
+ 0x04
+#else
+0
+#endif
+ | 
+#ifdef LM87_5V_VCC
+0x08
+#else   
+0
+#endif
+	);
+
+	data->vrm = 82;
+
+	/* Start monitoring */
+	reg = lm87_read_value(client, LM87_REG_CONFIG);
+	if (!(reg & 0x01)) {
+		printk(KERN_INFO "lm87.o: Monitoring starts\n");
+		lm87_write_value(client, LM87_REG_CONFIG,
+				 (reg & 0x7e) | 0x01);
+	}
+}
+
+static void lm87_update_client(struct i2c_client *client)
+{
+	struct lm87_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ) ||  /* 1 sec cache */
+            (jiffies < data->last_updated)      || 
+             !data->valid) {
+		for (i = 0; i <= 5; i++) {  
+		 data->in[i] = 
+		    lm87_read_value(client,LM87_REG_IN(i));
+		 data->in_min[i] = 
+		    lm87_read_value(client,LM87_REG_IN_MIN(i));
+		 data->in_max[i] = 
+		    lm87_read_value(client,LM87_REG_IN_MAX(i));
+		}
+		 data->ain1 = 
+		    lm87_read_value(client,LM87_REG_FAN1_AIN1);
+		 data->ain1_min =
+		    lm87_read_value(client,LM87_REG_AIN1_LOW);
+		 data->ain1_max =
+		    lm87_read_value(client,LM87_REG_FAN1_AIN1_LIMIT);
+		 data->ain2 = 
+		    lm87_read_value(client,LM87_REG_FAN2_AIN2);
+		 data->ain2_min =
+		    lm87_read_value(client,LM87_REG_AIN2_LOW);
+		 data->ain2_max =
+		    lm87_read_value(client,LM87_REG_FAN2_AIN2_LIMIT);
+
+		data->fan =
+		    lm87_read_value(client, LM87_REG_FAN1_AIN1);
+		data->fan_min =
+		    lm87_read_value(client, LM87_REG_FAN1_AIN1_LIMIT);
+		data->fan2 =
+		    lm87_read_value(client, LM87_REG_FAN2_AIN2);
+		data->fan2_min =
+		    lm87_read_value(client, LM87_REG_FAN2_AIN2_LIMIT);
+
+		data->ext2_temp =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2);
+		data->ext_temp =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1);
+		data->int_temp =
+		    lm87_read_value(client, LM87_REG_INT_TEMP);
+
+		data->ext2_temp_max =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH);
+		data->ext2_temp_min =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW);
+
+		data->ext_temp_max =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1_HIGH);
+		data->ext_temp_min =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1_LOW);
+
+		data->int_temp_max =
+		    lm87_read_value(client, LM87_REG_INT_TEMP_HIGH);
+		data->int_temp_min =
+		    lm87_read_value(client, LM87_REG_INT_TEMP_LOW);
+
+		i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+		data->fan_div = (i >> 4) & 0x03;
+		data->fan2_div = (i >> 6) & 0x03;
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (lm87_read_value(client, LM87_REG_VID4) & 0x01)
+		    << 4;
+		data->alarms =
+		    lm87_read_value(client, LM87_REG_INT1_STAT) +
+		    (lm87_read_value(client, LM87_REG_INT2_STAT) << 8);
+		data->analog_out =
+		    lm87_read_value(client, LM87_REG_ANALOG_OUT);
+		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. */
+void lm87_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	long scales[6] = { 250, 270, 
+#ifdef LM87_5V_VCC
+500,
+#else
+330,
+#endif
+		500, 1200, 270 };
+
+	struct lm87_data *data = client->data;
+	int nr = ctl_name - LM87_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = (data->in_min[nr] * scales[nr] + 96) / 192;
+		results[1] = (data->in_max[nr] * scales[nr] + 96) / 192;
+		results[2] = (data->in[nr] * scales[nr] + 96) / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = (results[0] * 192 + scales[nr] / 2)
+					 / scales[nr];
+			lm87_write_value(client, LM87_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = (results[1] * 192 + scales[nr] / 2)
+					 / scales[nr];
+			lm87_write_value(client, LM87_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+#if defined (LM87_AIN1) || defined (LM87_AIN2)
+void lm87_ain(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		if (ctl_name == LM87_SYSCTL_AIN1) {
+		 results[0] = data->ain1_min;
+		 results[1] = data->ain1_max;
+		 results[2] = data->ain1;
+		} else {
+		 results[0] = data->ain2_min;
+		 results[1] = data->ain2_max;
+		 results[2] = data->ain2;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+		 if (ctl_name == LM87_SYSCTL_AIN1) {
+			data->ain1_min = results[0];
+			lm87_write_value(client, LM87_REG_AIN1_LOW,
+					    data->ain1_min);
+		 } else {
+			data->ain2_min = results[0];
+			lm87_write_value(client, LM87_REG_AIN2_LOW,
+					    data->ain2_min);
+		 }
+		}
+		if (*nrels_mag >= 2) {
+		 if (ctl_name == LM87_SYSCTL_AIN1) {
+			data->ain1_max = results[1];
+			lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT,
+					    data->ain1_max);
+		 } else {
+			data->ain2_max = results[1];
+			lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT,
+					    data->ain2_max);
+		 }
+		}
+	}
+}
+#endif
+
+void lm87_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		if (ctl_name == LM87_SYSCTL_FAN1) {
+		 results[0] = FAN_FROM_REG(data->fan_min,
+					  DIV_FROM_REG(data->fan_div));
+		 results[1] = FAN_FROM_REG(data->fan, 
+		                         DIV_FROM_REG(data->fan_div));
+		} else {
+		 results[0] = FAN_FROM_REG(data->fan2_min,
+					  DIV_FROM_REG(data->fan2_div));
+		 results[1] = FAN_FROM_REG(data->fan2, 
+		                         DIV_FROM_REG(data->fan2_div));
+		}
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 0) {
+			if (ctl_name == LM87_SYSCTL_FAN1) {
+			 data->fan_min = FAN_TO_REG(results[0],
+						   DIV_FROM_REG
+						   (data->fan_div));
+			 lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT,
+					    data->fan_min);
+			} else {
+			 data->fan2_min = FAN_TO_REG(results[0],
+						   DIV_FROM_REG
+						   (data->fan2_div));
+			 lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT,
+					    data->fan2_min);
+			}
+		}
+	}
+}
+
+
+void lm87_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) 
+	{
+	   lm87_update_client(client);
+
+	   /* find out which temp. is being requested */
+	   if (ctl_name == LM87_SYSCTL_TEMP3) 
+	   {
+		results[0] = TEMP_FROM_REG(data->ext2_temp_max);
+		results[1] = TEMP_FROM_REG(data->ext2_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext2_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP2)
+	   {
+		results[0] = TEMP_FROM_REG(data->ext_temp_max);
+		results[1] = TEMP_FROM_REG(data->ext_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP1)
+	   {
+		results[0] = TEMP_FROM_REG(data->int_temp_max);
+		results[1] = TEMP_FROM_REG(data->int_temp_min);
+		results[2] = TEMP_FROM_REG(data->int_temp);
+	   }
+	   *nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+	           if (ctl_name == LM87_SYSCTL_TEMP3) {
+			data->ext2_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH,
+					    data->ext2_temp_max);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP2) {
+			data->ext_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH,
+					    data->ext_temp_max);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP1) {
+			data->int_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_INT_TEMP_HIGH,
+					    data->int_temp_max);
+	           }
+		}
+		if (*nrels_mag >= 2) {
+	           if (ctl_name == LM87_SYSCTL_TEMP3) {
+			data->ext2_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW,
+					    data->ext2_temp_min);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP2) {
+			data->ext_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW,
+					    data->ext_temp_min);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP1) {
+			data->int_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_INT_TEMP_LOW,
+					    data->int_temp_min);
+	           }
+		}
+	}
+}
+
+void lm87_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void lm87_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+/* This gets a little hairy depending on the hardware config */
+
+	struct lm87_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+#ifndef LM87_AIN1
+		results[0] = DIV_FROM_REG(data->fan_div);
+# ifndef LM87_AIN2
+		results[1] = DIV_FROM_REG(data->fan2_div);
+		*nrels_mag = 2;
+# else
+		*nrels_mag = 1;
+# endif
+#else /* Must be referring to fan 2 */
+		results[0] = DIV_FROM_REG(data->fan2_div);
+		*nrels_mag = 1;
+#endif
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+/* Note: it's OK to change fan2 div even if fan2 isn't enabled */
+#ifndef LM87_AIN1
+		if (*nrels_mag >= 2) {
+			data->fan2_div = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan2_div << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div << 4);
+			lm87_write_value(client, LM87_REG_VID_FAN_DIV, old);
+		}
+#else /* Must be referring to fan 2 */
+		if (*nrels_mag >= 1) {
+			data->fan2_div = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan2_div << 6);
+			lm87_write_value(client, LM87_REG_VID_FAN_DIV, old);
+		}
+#endif
+	}
+}
+
+void lm87_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = data->analog_out;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->analog_out = results[0];
+			lm87_write_value(client, LM87_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+
+void lm87_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void lm87_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+static int __init sm_lm87_init(void)
+{
+	printk(KERN_INFO "lm87.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&LM87_driver);
+}
+
+static void __exit sm_lm87_exit(void)
+{
+	i2c_del_driver(&LM87_driver);
+}
+
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, "
+     "Mark Studebaker <mdsxyz123@yahoo.com>, and Stephen Rousset <stephen.rousset@rocketlogix.com>");
+
+MODULE_DESCRIPTION("LM87 driver");
+
+module_init(sm_lm87_init);
+module_exit(sm_lm87_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/mtp008.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/mtp008.c	(revision 3147)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/mtp008.c	(revision 3147)
@@ -0,0 +1,1098 @@
+/*
+   mtp008.c - Part of lm_sensors, Linux kernel modules for hardware
+   monitoring
+   Copyright (C) 2001, 2004  Kris Van Hees <aedil@alchar.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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {SENSORS_I2C_END};
+static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END};
+static unsigned int normal_isa[] = {SENSORS_ISA_END};
+static unsigned int normal_isa_range[] = {SENSORS_ISA_END};
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(mtp008);
+
+/* The MTP008 registers */
+/*      in0 .. in6 */
+#define MTP008_REG_IN(nr)		(0x20 + (nr))
+#define MTP008_REG_IN_MAX(nr)		(0x2b + (nr) * 2)
+#define MTP008_REG_IN_MIN(nr)		(0x2c + (nr) * 2)
+
+/*      temp1 */
+#define MTP008_REG_TEMP			0x27
+#define MTP008_REG_TEMP_MAX		0x39
+#define MTP008_REG_TEMP_MIN		0x3a
+
+/*      fan1 .. fan3 */
+#define MTP008_REG_FAN(nr)		(0x27 + (nr))
+#define MTP008_REG_FAN_MIN(nr)		(0x3a + (nr))
+
+#define MTP008_REG_CONFIG		0x40
+#define MTP008_REG_INT_STAT1		0x41
+#define MTP008_REG_INT_STAT2		0x42
+
+#define MTP008_REG_SMI_MASK1		0x43
+#define MTP008_REG_SMI_MASK2		0x44
+
+#define MTP008_REG_NMI_MASK1		0x45
+#define MTP008_REG_NMI_MASK2		0x46
+
+#define MTP008_REG_VID_FANDIV		0x47
+
+#define MTP008_REG_I2C_ADDR		0x48
+
+#define MTP008_REG_RESET_VID4		0x49
+
+#define MTP008_REG_OVT_PROP		0x50
+
+#define MTP008_REG_BEEP_CTRL1		0x51
+#define MTP008_REG_BEEP_CTRL2		0x52
+
+/*      pwm1 .. pwm3  nr range 1-3 */
+#define MTP008_REG_PWM_CTRL(nr)		(0x52 + (nr))
+
+#define MTP008_REG_PIN_CTRL1		0x56
+#define MTP008_REG_PIN_CTRL2		0x57
+
+#define MTP008_REG_CHIPID		0x58
+
+/*
+ * Pin control register configuration constants.
+ */
+#define MTP008_CFG_VT1_PII		0x08
+#define MTP008_CFG_VT2_AIN		0x00
+#define MTP008_CFG_VT2_VT		0x03
+#define MTP008_CFG_VT2_PII		0x04
+#define MTP008_CFG_VT2_MASK		0x06
+#define MTP008_CFG_VT3_VT		0x01
+
+/* sensor pin types */
+#define VOLTAGE		1
+#define THERMISTOR	2
+#define PIIDIODE	3
+
+/*
+ * Conversion routines and macros.  Limit checking is only done on
+ * the TO_REG variants.
+ *
+ * Note that IN values are expressed as 100 times the actual voltage to avoid
+ * having to use floating point values.  As such, IN values are between 0 and
+ * 409 (0V to 4.096V).
+ */
+#define IN_TO_REG(val)		(SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255))
+#define IN_FROM_REG(val)	(((val) * 16 + 5) / 10)
+
+/*
+ * The fan cotation count (as stored in the register) is calculated using the
+ * following formula:
+ *      count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div)
+ * and the rpm is therefore:
+ *      rpm = 1350000 / (count * div)
+ */
+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))  \
+				)
+
+/*
+ * Temperatures are stored as two's complement values of the Celsius value.  It
+ * actually uses 10 times the Celsius value to avoid using floating point
+ * values.
+ */
+#define TEMP_TO_REG(val)	(					      \
+				 (val) < 0				      \
+				    ? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \
+				    : SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \
+				)
+#define TEMP_FROM_REG(val)	(					      \
+				 (					      \
+				  (val) > 0x80 ? (val) - 0x100		      \
+					       : (val)			      \
+				 ) * 10					      \
+				)
+
+/*
+ * VCORE voltage:
+ *      0x00 to 0x0f    = 2.05 to 1.30 (0.05 per unit)
+ *      0x10 to 0x1e    = 3.50 to 2.10 (0.10 per unit)
+ *      0x1f            = No CPU
+ */
+#define VID_FROM_REG(val)	((val) == 0x1f				      \
+					 ? 0				      \
+					 : (val) < 0x10 ? 205 - (val) * 5     \
+							: 510 - (val) * 10)
+
+/*
+ * Fan divider.
+ */
+#define DIV_FROM_REG(val)	(1 << (val))
+#define DIV_TO_REG(val)		((val) == 8 ? 3				      \
+					    : (val) == 4 ? 2		      \
+							 : (val) == 2 ? 1     \
+								      : 0)
+
+/*
+ * Alarms (interrupt status).
+ */
+#define ALARMS_FROM_REG(val)	(val)
+
+/*
+ * Beep controls.
+ */
+#define BEEPS_FROM_REG(val)	(val)
+#define BEEPS_TO_REG(val)	(val)
+
+/*
+ * PWM control. nr range 1 to 3
+ */
+#define PWM_FROM_REG(val)	(val)
+#define PWM_TO_REG(val)		(val)
+#define PWMENABLE_FROM_REG(nr, val)	(((val) >> ((nr) + 3)) & 1)
+
+/*
+ * For each registered MTP008, we need to keep some data in memory.  The
+ * structure itself is dynamically allocated, at the same time when a new
+ * mtp008 client is allocated.
+ */
+struct mtp008_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;				/* !=0 if fields are valid */
+	unsigned long last_updated;		/* In jiffies */
+
+	u8 in[7];				/* Register value */
+	u8 in_max[7];				/* Register value */
+	u8 in_min[7];				/* Register value */
+	u8 temp;				/* Register value */
+	u8 temp_max;				/* Register value */
+	u8 temp_min;				/* Register value */
+	u8 fan[3];				/* Register value */
+	u8 fan_min[3];				/* Register value */
+	u8 vid;					/* Register encoding */
+	u8 fan_div[3];				/* Register encoding */
+	u16 alarms;				/* Register encoding */
+	u16 beeps;				/* Register encoding */
+	u8 pwm[4];				/* Register value */
+	u8 sens[3];				/* 1 = Analog input,
+						   2 = Thermistor,
+						   3 = PII/Celeron diode */
+	u8 pwmenable;				/* Register 0x57 value */
+};
+
+static int mtp008_attach_adapter(struct i2c_adapter *adapter);
+static int mtp008_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int mtp008_detach_client(struct i2c_client *client);
+
+static int mtp008_read_value(struct i2c_client *client, u8 register);
+static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value);
+static void mtp008_update_client(struct i2c_client *client);
+static void mtp008_init_client(struct i2c_client *client);
+
+static void mtp008_in(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void mtp008_fan(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_temp_add(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void mtp008_vid(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_fan_div(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void mtp008_alarms(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void mtp008_beep(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_pwm(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_sens(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_getsensortype(struct mtp008_data *data, u8 inp);
+
+static struct i2c_driver mtp008_driver =
+{
+	.name		= "MTP008 sensor driver",
+	.id		= I2C_DRIVERID_MTP008,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= mtp008_attach_adapter,
+	.detach_client	= mtp008_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define MTP008_SYSCTL_IN0	1000	/* Volts * 100 */
+#define MTP008_SYSCTL_IN1	1001
+#define MTP008_SYSCTL_IN2	1002
+#define MTP008_SYSCTL_IN3	1003
+#define MTP008_SYSCTL_IN4	1004
+#define MTP008_SYSCTL_IN5	1005
+#define MTP008_SYSCTL_IN6	1006
+#define MTP008_SYSCTL_FAN1	1101	/* Rotations/min */
+#define MTP008_SYSCTL_FAN2	1102
+#define MTP008_SYSCTL_FAN3	1103
+#define MTP008_SYSCTL_TEMP1	1200	/* Degrees Celsius * 10 */
+#define MTP008_SYSCTL_TEMP2	1201	/* Degrees Celsius * 10 */
+#define MTP008_SYSCTL_TEMP3	1202	/* Degrees Celsius * 10 */
+#define MTP008_SYSCTL_VID	1300	/* Volts * 100 */
+#define MTP008_SYSCTL_PWM1	1401
+#define MTP008_SYSCTL_PWM2	1402
+#define MTP008_SYSCTL_PWM3	1403
+#define MTP008_SYSCTL_SENS1	1501	/* 1, 2, or Beta (3000-5000) */
+#define MTP008_SYSCTL_SENS2	1502
+#define MTP008_SYSCTL_SENS3	1503
+#define MTP008_SYSCTL_FAN_DIV	2000	/* 1, 2, 4 or 8 */
+#define MTP008_SYSCTL_ALARMS	2001	/* bitvector */
+#define MTP008_SYSCTL_BEEP	2002	/* bitvector */
+
+#define MTP008_ALARM_IN0	0x0001
+#define MTP008_ALARM_IN1	0x0002
+#define MTP008_ALARM_IN2	0x0004
+#define MTP008_ALARM_IN3	0x0008
+#define MTP008_ALARM_IN4	0x0100
+#define MTP008_ALARM_IN5	0x0200
+#define MTP008_ALARM_IN6	0x0400
+#define MTP008_ALARM_FAN1	0x0040
+#define MTP008_ALARM_FAN2	0x0080
+#define MTP008_ALARM_FAN3	0x0800
+#define MTP008_ALARM_TEMP1	0x0010
+#define MTP008_ALARM_TEMP2	0x0100
+#define MTP008_ALARM_TEMP3	0x0200
+
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected chip. 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 mtp008_dir_table_template[] =
+{
+	{MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp},
+	{MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid},
+	{MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div},
+	{MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms},
+	{MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep},
+	{MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{0}
+};
+
+/* This function is called when:
+ * mtp008_driver is inserted (when this module is loaded), for each available
+ * adapter when a new adapter is inserted (and mtp008_driver is still present)
+ */
+static int mtp008_attach_adapter(struct i2c_adapter *adapter)
+{
+	struct i2c_client_address_data mtp008_addr_data;
+
+	mtp008_addr_data.normal_i2c = addr_data.normal_i2c;
+	mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range;
+	mtp008_addr_data.probe = addr_data.probe;
+	mtp008_addr_data.probe_range = addr_data.probe_range;
+	mtp008_addr_data.ignore = addr_data.ignore;
+	mtp008_addr_data.ignore_range = addr_data.ignore_range;
+	mtp008_addr_data.force = addr_data.forces->force;
+
+	return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect);
+}
+
+int mtp008_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa, err, sysid;
+	struct i2c_client *new_client;
+	struct mtp008_data *data;
+
+	err = 0;
+
+	is_isa = i2c_is_isa_adapter(adapter);
+	if (is_isa ||
+	    !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/*
+	 * 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 use mtp008_(read|write)_value().
+	 */
+	if (!(data = kmalloc(sizeof(struct mtp008_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &mtp008_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Remaining detection.
+	 */
+	if (kind < 0) {
+		if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac)
+			goto ERROR1;
+	}
+	/*
+	 * Fill in the remaining client fields and put it into the global list.
+	 */
+	type_name = "mtp008";
+	client_name = "MTP008 chip";
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer that a new client has arrived.
+	 */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/*
+	 * Register a new directory entry with the sensors module.
+	 */
+	if ((sysid = i2c_register_entry(new_client, type_name,
+					    mtp008_dir_table_template,
+					    THIS_MODULE)) < 0) {
+		err = sysid;
+		goto ERROR2;
+	}
+	data->sysctl_id = sysid;
+
+	/*
+	 * Initialize the MTP008 chip.
+	 */
+	mtp008_init_client(new_client);
+
+	return 0;
+
+	/*
+	 * Error handling.  Bad programming practise but very code efficient.
+	 */
+      ERROR2:
+	i2c_detach_client(new_client);
+      ERROR1:
+	kfree(data);
+
+      ERROR0:
+	return err;
+}
+
+static int mtp008_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(
+		((struct mtp008_data *) (client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("mtp008.o: Deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+
+	return 0;
+}
+
+
+static int mtp008_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg) & 0xff;
+}
+
+static int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new MTP008. */
+static void mtp008_init_client(struct i2c_client *client)
+{
+	u8 save1, save2;
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	/*
+	 * Initialize the Myson MTP008 hardware monitoring chip.
+	 * Save the pin settings that the BIOS hopefully set.
+	 */
+	save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
+	save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+	mtp008_write_value(client, MTP008_REG_CONFIG,
+	     (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80);
+	mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1);
+	mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2);
+
+	mtp008_getsensortype(data, save2);
+
+
+	/*
+	 * Start monitoring.
+	 */
+	mtp008_write_value(
+		client, MTP008_REG_CONFIG,
+		(mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01
+	);
+}
+
+static void mtp008_update_client(struct i2c_client *client)
+{
+	int i;
+	u8 inp;
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk("Starting MTP008 update\n");
+#endif
+
+		/*
+		 * Read in the analog inputs.  We're reading AIN4 and AIN5 as
+		 * regular analog inputs, even though they may have been
+		 * configured as temperature readings instead.  Interpretation
+		 * of these values is done elsewhere.
+		 */
+		for (i = 0; i < 7; i++) {
+			data->in[i] =
+				mtp008_read_value(client, MTP008_REG_IN(i));
+			data->in_max[i] =
+				mtp008_read_value(client, MTP008_REG_IN_MAX(i));
+			data->in_min[i] =
+				mtp008_read_value(client, MTP008_REG_IN_MIN(i));
+		}
+
+		/*
+		 * Read the temperature sensor.
+		 */
+		data->temp = mtp008_read_value(client, MTP008_REG_TEMP);
+		data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX);
+		data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN);
+
+		/*
+		 * Read the first 2 fan dividers and the VID setting.  Read the
+		 * third fan divider from a different register.
+		 */
+		inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
+		data->vid = inp & 0x0f;
+		data->vid |= (mtp008_read_value(client,
+				     MTP008_REG_RESET_VID4) & 0x01) << 4;
+
+		data->fan_div[0] = (inp >> 4) & 0x03;
+		data->fan_div[1] = inp >> 6;
+		data->fan_div[2] =
+			mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6;
+
+		/*
+		 * Read the interrupt status registers.
+		 */
+		data->alarms =
+			(mtp008_read_value(client,
+					   MTP008_REG_INT_STAT1) & 0xdf) |
+			(mtp008_read_value(client,
+					   MTP008_REG_INT_STAT2) & 0x0f) << 8;
+
+		/*
+		 * Read the beep control registers.
+		 */
+		data->beeps =
+			(mtp008_read_value(client,
+					   MTP008_REG_BEEP_CTRL1) & 0xdf) |
+			(mtp008_read_value(client,
+					   MTP008_REG_BEEP_CTRL2) & 0x8f) << 8;
+
+		/*
+		 * Read the sensor configuration.
+		 */
+		inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+		mtp008_getsensortype(data, inp);
+		data->pwmenable = inp;
+
+		/*
+		 * Read the PWM registers if enabled.
+		 */
+		for (i = 1; i <= 3; i++)
+		{
+			if(PWMENABLE_FROM_REG(i, inp))
+				data->pwm[i-1] = mtp008_read_value(client,
+						  MTP008_REG_PWM_CTRL(i));
+			else
+				data->pwm[i-1] = 255;
+		}
+
+		/*
+		 * Read the fan sensors. Skip 3 if PWM1 enabled.
+		 */
+		for (i = 1; i <= 3; i++) {
+			if(i == 3 && PWMENABLE_FROM_REG(1, inp)) {
+				data->fan[2] = 0;
+				data->fan_min[2] = 0;
+			} else {
+				data->fan[i-1] = mtp008_read_value(client,
+					  MTP008_REG_FAN(i));
+				data->fan_min[i-1] = mtp008_read_value(client,
+					  MTP008_REG_FAN_MIN(i));
+			}
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+	up(&data->update_lock);
+}
+
+static void mtp008_getsensortype(struct mtp008_data *data, u8 inp)
+{
+	inp &= 0x0f;
+	data->sens[0] = (inp >> 3) + 2;			/* 2 or 3 */
+	data->sens[1] = ((inp >> 1) & 0x03) + 1;	/* 1, 2 or 3 */
+	data->sens[2] = (inp & 0x01) + 1;		/* 1 or 2 */
+}
+
+/* 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. */
+void mtp008_in(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_IN0;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
+			results[0] = IN_FROM_REG(data->in_min[nr]);
+			results[1] = IN_FROM_REG(data->in_max[nr]);
+			results[2] = IN_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
+			if (*nrels_mag >= 1) {
+				data->in_min[nr] = IN_TO_REG(results[0]);
+				mtp008_write_value(client, MTP008_REG_IN_MIN(nr),
+						   data->in_min[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_max[nr] = IN_TO_REG(results[1]);
+				mtp008_write_value(client, MTP008_REG_IN_MAX(nr),
+						   data->in_max[nr]);
+			}
+		}
+	}
+}
+
+void mtp008_fan(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_FAN1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = FAN_FROM_REG(data->fan_min[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+		results[1] = FAN_FROM_REG(data->fan[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] =
+			    FAN_TO_REG(results[0],
+				       DIV_FROM_REG(data->fan_div[nr]));
+			mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1),
+					   data->fan_min[nr]);
+		}
+	}
+}
+
+void mtp008_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = TEMP_FROM_REG(data->temp_max);
+		results[1] = TEMP_FROM_REG(data->temp_min);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->temp_max = TEMP_TO_REG(results[0]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MAX,
+					   data->temp_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_min = TEMP_TO_REG(results[1]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MIN,
+					   data->temp_min);
+		}
+	}
+}
+
+void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name,
+		     int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1;	/* AIN4 or AIN5 */
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		if(data->sens[nr - 3] != VOLTAGE) {
+			results[0] = TEMP_FROM_REG(data->in_max[nr]);
+			results[1] = TEMP_FROM_REG(data->in_min[nr]);
+			results[2] = TEMP_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if(data->sens[nr - 3] != VOLTAGE) {
+			if (*nrels_mag >= 1) {
+				data->in_max[nr] = TEMP_TO_REG(results[0]);
+				mtp008_write_value(client,
+						   MTP008_REG_IN_MAX(nr),
+						   data->in_max[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_min[nr] = TEMP_TO_REG(results[1]);
+				mtp008_write_value(client,
+						   MTP008_REG_IN_MIN(nr),
+						   data->in_min[nr]);
+			}
+		}
+	}
+}
+
+void mtp008_vid(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = VID_FROM_REG(data->vid);
+
+		*nrels_mag = 1;
+	}
+}
+
+void mtp008_fan_div(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+	u8 val;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = DIV_FROM_REG(data->fan_div[2]);
+
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 3) {
+			data->fan_div[2] = DIV_TO_REG(results[2]);
+			val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
+			val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6;
+			mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val);
+		}
+		if (*nrels_mag >= 1) {
+			val = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
+			if (*nrels_mag >= 2) {
+				data->fan_div[1] = DIV_TO_REG(results[1]);
+				val = (val & 0x3f) |
+				      (data->fan_div[1] & 0x03) << 6;
+			}
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4;
+			mtp008_write_value(client, MTP008_REG_VID_FANDIV, val);
+		}
+	}
+}
+
+void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = ALARMS_FROM_REG(data->alarms);
+
+		*nrels_mag = 1;
+	}
+}
+
+void mtp008_beep(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = BEEPS_FROM_REG(data->beeps);
+
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f;
+
+			mtp008_write_value(client, MTP008_REG_BEEP_CTRL1,
+					   data->beeps & 0xff);
+			mtp008_write_value(client, MTP008_REG_BEEP_CTRL2,
+					   data->beeps >> 8);
+		}
+	}
+}
+
+void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_PWM1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = PWM_FROM_REG(data->pwm[nr]);
+		results[1] = PWMENABLE_FROM_REG(nr + 1, data->pwmenable);
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			if (*nrels_mag >= 2) {
+				if(results[1])
+					data->pwmenable |= 0x10 << nr;
+				else
+					data->pwmenable &= ~(0x10 << nr);
+				mtp008_write_value(client, MTP008_REG_PIN_CTRL2,
+					           data->pwmenable);
+			}
+			data->pwm[nr] = PWM_TO_REG(results[0]);
+			mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr),
+					   data->pwm[nr]);
+		}
+	}
+}
+
+void mtp008_sens(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	const char *opts = "";
+	int nr;
+	u8 tmp;
+	struct mtp008_data *data;
+
+	nr = 1 + ctl_name - MTP008_SYSCTL_SENS1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		results[0] = data->sens[nr - 1];
+
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+
+			switch (nr) {
+			case 1:	/* VT or PII */
+				opts = "2 or 3";
+
+				switch (results[0]) {
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT1_PII);
+					data->sens[0] = 2;
+					return;
+				case PIIDIODE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT1_PII);
+					data->sens[0] = 3;
+					return;
+				}
+
+				break;
+			case 2:	/* AIN, VT or PII */
+				tmp &= ~MTP008_CFG_VT2_MASK;
+				opts = "1, 2 or 3";
+
+				switch (results[0]) {
+				case VOLTAGE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_AIN);
+					data->sens[1] = 1;
+					return;
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_VT);
+					data->sens[1] = 2;
+					return;
+				case PIIDIODE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_PII);
+					data->sens[1] = 3;
+					return;
+				}
+
+				break;
+			case 3:	/* AIN or VT */
+				opts = "1 or 2";
+
+				switch (results[0]) {
+				case VOLTAGE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT3_VT);
+					data->sens[2] = 1;
+					return;
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT3_VT);
+					data->sens[2] = 2;
+					return;
+				}
+
+				break;
+			}
+
+			printk("mtp008.o: Invalid sensor type %ld "
+			       "for sensor %d; must be %s.\n",
+			       results[0], nr, opts);
+		}
+	}
+}
+
+static int __init sm_mtp008_init(void)
+{
+	printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&mtp008_driver);
+}
+
+static void __exit sm_mtp008_exit(void)
+{
+	i2c_del_driver(&mtp008_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Kris Van Hees <aedil@alchar.org>");
+MODULE_DESCRIPTION("MTP008 driver");
+
+module_init(sm_mtp008_init);
+module_exit(sm_mtp008_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscscy.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscscy.c	(revision 3147)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscscy.c	(revision 3147)
@@ -0,0 +1,910 @@
+/*
+    fscscy.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de>
+
+    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.
+*/
+
+/* 
+    fujitsu siemens scylla chip, 
+    module based on lm80.c, fscpos.c
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(fscscy);
+
+/* The FSCSCY registers */
+
+/* chip identification */
+#define FSCSCY_REG_IDENT_0    0x00
+#define FSCSCY_REG_IDENT_1    0x01
+#define FSCSCY_REG_IDENT_2    0x02
+#define FSCSCY_REG_REVISION   0x03
+
+/* global control and status */
+#define FSCSCY_REG_EVENT_STATE  0x04
+#define FSCSCY_REG_CONTROL       0x05
+
+/* watchdog */
+#define FSCSCY_REG_WDOG_PRESET      0x28
+#define FSCSCY_REG_WDOG_STATE       0x23
+#define FSCSCY_REG_WDOG_CONTROL     0x21
+
+/*
+** Fan definitions
+**
+** _RPMMIN: Minimum speed. Can be set via interface, but only for three of the fans
+**          FAN1_RPMMIN is wired to Fan 0 (CPU Fans)
+**          FAN4_RPMMIN is wired to Fan 2 (PS Fans ??)
+**          FAN5_RPMMIN is wired to Fan 3 (AUX Fans ??)
+** _ACT:    Actual Fan Speed
+** _STATE:  Fan status register
+** _RIPPLE: Fan speed multiplier
+*/
+
+/* fan 0  */
+#define FSCSCY_REG_FAN0_RPMMIN	0x65
+#define FSCSCY_REG_FAN0_ACT	0x6b
+#define FSCSCY_REG_FAN0_STATE	0x62
+#define FSCSCY_REG_FAN0_RIPPLE	0x6f
+
+/* fan 1  */
+#define FSCSCY_REG_FAN1_RPMMIN     FSCSCY_REG_FAN0_RPMMIN
+#define FSCSCY_REG_FAN1_ACT     0x6c
+#define FSCSCY_REG_FAN1_STATE   0x61
+#define FSCSCY_REG_FAN1_RIPPLE  0x6f
+
+/* fan 2  */
+#define FSCSCY_REG_FAN2_RPMMIN     0x55
+#define FSCSCY_REG_FAN2_ACT     0x0e
+#define FSCSCY_REG_FAN2_STATE   0x0d
+#define FSCSCY_REG_FAN2_RIPPLE  0x0f
+
+/* fan 3  */
+#define FSCSCY_REG_FAN3_RPMMIN     0xa5
+#define FSCSCY_REG_FAN3_ACT     0xab
+#define FSCSCY_REG_FAN3_STATE   0xa2
+#define FSCSCY_REG_FAN3_RIPPLE  0xaf
+
+/* fan 4  */
+#define FSCSCY_REG_FAN4_RPMMIN     FSCSCY_REG_FAN2_RPMMIN
+#define FSCSCY_REG_FAN4_ACT	0x5c
+#define FSCSCY_REG_FAN4_STATE   0x52
+#define FSCSCY_REG_FAN4_RIPPLE  0x0f
+
+/* fan 5  */
+#define FSCSCY_REG_FAN5_RPMMIN     FSCSCY_REG_FAN3_RPMMIN
+#define FSCSCY_REG_FAN5_ACT     0xbb
+#define FSCSCY_REG_FAN5_STATE   0xb2
+#define FSCSCY_REG_FAN5_RIPPLE  0xbf
+
+/* voltage supervision */
+#define FSCSCY_REG_VOLT_12       0x45
+#define FSCSCY_REG_VOLT_5        0x42
+#define FSCSCY_REG_VOLT_BATT     0x48
+
+/* temperatures */
+/* sensor 0 */
+#define FSCSCY_REG_TEMP0_ACT	0x64
+#define FSCSCY_REG_TEMP0_STATE	0x71
+#define FSCSCY_REG_TEMP0_LIM	0x76
+
+/* sensor 1 */
+#define FSCSCY_REG_TEMP1_ACT	0xD0
+#define FSCSCY_REG_TEMP1_STATE	0xD1
+#define FSCSCY_REG_TEMP1_LIM	0xD6
+
+/* sensor 2 */
+#define FSCSCY_REG_TEMP2_ACT	0x32
+#define FSCSCY_REG_TEMP2_STATE	0x81
+#define FSCSCY_REG_TEMP2_LIM	0x86
+
+/* sensor3 */
+#define FSCSCY_REG_TEMP3_ACT	0x35
+#define FSCSCY_REG_TEMP3_STATE	0x91
+#define FSCSCY_REG_TEMP3_LIM	0x96
+
+/* PCI Load */
+#define FSCSCY_REG_PCILOAD	0x1a
+
+/* Intrusion Sensor */
+#define FSCSCY_REG_INTR_STATE	0x13
+#define FSCSCY_REG_INTR_CTRL	0x12
+
+/* 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,nr) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+/* Initial limits */
+
+/* For each registered FSCSCY, we need to keep some data in memory. That
+   data is pointed to by fscscy_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new fscscy client is
+   allocated. */
+struct fscscy_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  revision;        /* revision of chip */
+	u8  global_event;    /* global event status */
+	u8  global_control;  /* global control register */
+	u8  watchdog[3];     /* watchdog */
+	u8  volt[3];         /* 12, 5, battery current */ 
+	u8  volt_min[3];     /* minimum voltages over module "lifetime" */
+	u8  volt_max[3];     /* maximum voltages over module "lifetime" */
+	u8  temp_act[4];     /* temperature */
+	u8  temp_status[4];  /* status of temp. sensor */
+	u8  temp_lim[4];     /* limit temperature of temp. sensor */
+	u8  temp_min[4];     /* minimum of temp. sensor, this is just calculated by the module */
+	u8  temp_max[4];     /* maximum of temp. sensor, this is just calculsted by the module */
+	u8  fan_act[6];      /* fans revolutions per second */
+	u8  fan_status[6];   /* fan status */
+	u8  fan_rpmmin[6];   /* fan min value for rps */
+	u8  fan_ripple[6];   /* divider for rps */
+	u8  fan_min[6];      /* minimum RPM over module "lifetime" */
+	u8  fan_max[6];      /* maximum RPM over module "lifetime" */
+	u8  pciload;	     /* PCILoad value */
+	u8  intr_status;     /* Intrusion Status */
+	u8  intr_control;    /* Intrusion Control */
+};
+
+
+static int fscscy_attach_adapter(struct i2c_adapter *adapter);
+static int fscscy_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int fscscy_detach_client(struct i2c_client *client);
+
+static int fscscy_read_value(struct i2c_client *client, u8 register);
+static int fscscy_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void fscscy_update_client(struct i2c_client *client);
+static void fscscy_init_client(struct i2c_client *client);
+
+
+static void fscscy_in(struct i2c_client *client, int operation, int ctl_name,
+		    	int *nrels_mag, long *results);
+static void fscscy_fan(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_fan_internal(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results, 
+		     	int nr, int reg_state, int reg_min, int res_ripple);
+static void fscscy_temp(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_volt(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_wdog(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_pciload(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_intrusion(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver fscscy_driver = {
+	.name		= "FSCSCY sensor driver",
+	.id		= I2C_DRIVERID_FSCSCY,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= fscscy_attach_adapter,
+	.detach_client	= fscscy_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define FSCSCY_SYSCTL_VOLT0    1000       /* 12 volt supply */
+#define FSCSCY_SYSCTL_VOLT1    1001       /* 5 volt supply */
+#define FSCSCY_SYSCTL_VOLT2    1002       /* batterie voltage*/
+#define FSCSCY_SYSCTL_FAN0     1101       /* state, min, ripple, actual value fan 0 */
+#define FSCSCY_SYSCTL_FAN1     1102       /* state, min, ripple, actual value fan 1 */
+#define FSCSCY_SYSCTL_FAN2     1103       /* state, min, ripple, actual value fan 2 */
+#define FSCSCY_SYSCTL_FAN3     1104       /* state, min, ripple, actual value fan 3 */
+#define FSCSCY_SYSCTL_FAN4     1105       /* state, min, ripple, actual value fan 4 */
+#define FSCSCY_SYSCTL_FAN5     1106       /* state, min, ripple, actual value fan 5 */
+#define FSCSCY_SYSCTL_TEMP0    1201       /* state and value of sensor 0, cpu die */
+#define FSCSCY_SYSCTL_TEMP1    1202       /* state and value of sensor 1, motherboard */
+#define FSCSCY_SYSCTL_TEMP2    1203       /* state and value of sensor 2, chassis */
+#define FSCSCY_SYSCTL_TEMP3    1204       /* state and value of sensor 3, chassis */
+#define FSCSCY_SYSCTL_REV     2000        /* Revision */
+#define FSCSCY_SYSCTL_EVENT   2001        /* global event status */
+#define FSCSCY_SYSCTL_CONTROL 2002        /* global control byte */
+#define FSCSCY_SYSCTL_WDOG     2003       /* state, min, ripple, actual value fan 2 */
+#define FSCSCY_SYSCTL_PCILOAD  2004       /* PCILoad value */
+#define FSCSCY_SYSCTL_INTRUSION 2005      /* state, control for intrusion sensor */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected FSCSCY. 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 fscscy_dir_table_template[] = {
+	{FSCSCY_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP3, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN3, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN4, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN5, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_wdog},
+	{FSCSCY_SYSCTL_PCILOAD, "pciload", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_pciload},
+	{FSCSCY_SYSCTL_INTRUSION, "intrusion", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_intrusion},
+	{0}
+};
+
+static int fscscy_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, fscscy_detect);
+}
+
+int fscscy_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct fscscy_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("fscscy.o: fscscy_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* 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 fscscy_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct fscscy_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &fscscy_driver;
+	new_client->flags = 0;
+
+	/* Do the remaining detection unless force or force_fscscy parameter */
+	if (kind < 0) {
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_0) != 0x53)
+			goto ERROR1;
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_1) != 0x43)
+			goto ERROR1;
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_2) != 0x59)
+			goto ERROR1;
+	}
+
+	kind = fscscy;
+
+	type_name = "fscscy";
+	client_name = "fsc scylla chip";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					fscscy_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	fscscy_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int fscscy_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct fscscy_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("fscscy.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int fscscy_read_value(struct i2c_client *client, u8 reg)
+{
+#ifdef DEBUG
+	printk("fscscy: read reg 0x%02x\n",reg);
+#endif
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscscy_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+#ifdef DEBUG
+	printk("fscscy: write reg 0x%02x, val 0x%02x\n",reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCSCY. */
+static void fscscy_init_client(struct i2c_client *client)
+{
+	struct fscscy_data *data = client->data;
+
+	/* read revision from chip */
+	data->revision =  fscscy_read_value(client,FSCSCY_REG_REVISION);
+
+        /* Initialize min/max values from chip */
+	data->fan_min[0]  = data->fan_max[0]  = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT);
+	data->fan_min[1]  = data->fan_max[1]  = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT);
+	data->fan_min[2]  = data->fan_max[2]  = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT);
+	data->fan_min[3]  = data->fan_max[3]  = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT);
+	data->fan_min[4]  = data->fan_max[4]  = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT);
+	data->fan_min[4]  = data->fan_max[5]  = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT);
+        data->temp_min[0] = data->temp_max[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT);
+        data->temp_min[1] = data->temp_max[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT);
+        data->temp_min[2] = data->temp_max[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT);
+        data->temp_min[3] = data->temp_max[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT);
+	data->volt_min[0] = data->volt_max[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12);
+	data->volt_min[1] = data->volt_max[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5);
+	data->volt_min[2] = data->volt_max[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT);
+}
+
+static void fscscy_update_client(struct i2c_client *client)
+{
+	struct fscscy_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting fscscy update\n");
+#endif
+		data->temp_act[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT);
+		  if (data->temp_min[0] > data->temp_act[0]) data->temp_min[0] = data->temp_act[0];
+		  if (data->temp_max[0] < data->temp_act[0]) data->temp_max[0] = data->temp_act[0];
+		data->temp_act[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT);
+		  if (data->temp_min[1] > data->temp_act[1]) data->temp_min[1] = data->temp_act[1];
+		  if (data->temp_max[1] < data->temp_act[1]) data->temp_max[1] = data->temp_act[1];
+		data->temp_act[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT);
+		  if (data->temp_min[2] > data->temp_act[2]) data->temp_min[2] = data->temp_act[2];
+		  if (data->temp_max[2] < data->temp_act[2]) data->temp_max[2] = data->temp_act[2];
+		data->temp_act[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT);
+		  if (data->temp_min[3] > data->temp_act[3]) data->temp_min[3] = data->temp_act[3];
+		  if (data->temp_max[3] < data->temp_act[3]) data->temp_max[3] = data->temp_act[3];
+		data->temp_status[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_STATE);
+		data->temp_status[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_STATE);
+		data->temp_status[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_STATE);
+		data->temp_status[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_STATE);
+		data->temp_lim[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_LIM);
+		data->temp_lim[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_LIM);
+		data->temp_lim[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_LIM);
+		data->temp_lim[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_LIM);
+
+		data->volt[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12);
+		  if (data->volt_min[0] > data->volt[0]) data->volt_min[0] = data->volt[0];
+		  if (data->volt_max[0] < data->volt[0]) data->volt_max[0] = data->volt[0];
+		data->volt[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5);
+		  if (data->volt_min[1] > data->volt[1]) data->volt_min[1] = data->volt[1];
+		  if (data->volt_max[1] < data->volt[1]) data->volt_max[1] = data->volt[1];
+		data->volt[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT);
+		  if (data->volt_min[2] > data->volt[2]) data->volt_min[2] = data->volt[2];
+		  if (data->volt_max[2] < data->volt[2]) data->volt_max[2] = data->volt[2];
+
+		data->fan_act[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT);
+		  if (data->fan_min[0] > data->fan_act[0]) data->fan_min[0] = data->fan_act[0];
+		  if (data->fan_max[0] < data->fan_act[0]) data->fan_max[0] = data->fan_act[0];
+		data->fan_act[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT);
+		  if (data->fan_min[1] > data->fan_act[1]) data->fan_min[1] = data->fan_act[1];
+		  if (data->fan_max[1] < data->fan_act[1]) data->fan_max[1] = data->fan_act[1];
+		data->fan_act[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT);
+		  if (data->fan_min[2] > data->fan_act[2]) data->fan_min[2] = data->fan_act[2];
+		  if (data->fan_max[2] < data->fan_act[2]) data->fan_max[2] = data->fan_act[2];
+		data->fan_act[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT);
+		  if (data->fan_min[3] > data->fan_act[3]) data->fan_min[3] = data->fan_act[3];
+		  if (data->fan_max[3] < data->fan_act[3]) data->fan_max[3] = data->fan_act[3];
+		data->fan_act[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT);
+		  if (data->fan_min[4] > data->fan_act[4]) data->fan_min[4] = data->fan_act[4];
+		  if (data->fan_max[4] < data->fan_act[4]) data->fan_max[4] = data->fan_act[4];
+		data->fan_act[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT);
+		  if (data->fan_min[5] > data->fan_act[5]) data->fan_min[5] = data->fan_act[5];
+		  if (data->fan_max[5] < data->fan_act[5]) data->fan_max[5] = data->fan_act[5];
+		data->fan_status[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_STATE);
+		data->fan_status[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_STATE);
+		data->fan_status[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_STATE);
+		data->fan_status[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_STATE);
+		data->fan_status[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_STATE);
+		data->fan_status[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_STATE);
+		data->fan_rpmmin[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RPMMIN);
+		data->fan_rpmmin[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RPMMIN);
+		data->fan_rpmmin[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RPMMIN);
+		data->fan_rpmmin[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RPMMIN);
+		data->fan_rpmmin[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RPMMIN);
+		data->fan_rpmmin[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RPMMIN);
+		data->fan_ripple[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RIPPLE);
+		data->fan_ripple[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RIPPLE);
+		data->fan_ripple[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RIPPLE);
+		data->fan_ripple[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RIPPLE);
+		data->fan_ripple[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RIPPLE);
+		data->fan_ripple[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RIPPLE);
+
+		data->watchdog[0] = fscscy_read_value(client, FSCSCY_REG_WDOG_PRESET);
+		data->watchdog[1] = fscscy_read_value(client, FSCSCY_REG_WDOG_STATE);
+		data->watchdog[2] = fscscy_read_value(client, FSCSCY_REG_WDOG_CONTROL);
+
+		data->global_event = fscscy_read_value(client, FSCSCY_REG_EVENT_STATE);
+		data->global_control = fscscy_read_value(client, FSCSCY_REG_CONTROL);
+		data->pciload = fscscy_read_value(client, FSCSCY_REG_PCILOAD);
+		data->intr_status = fscscy_read_value(client, FSCSCY_REG_INTR_STATE);
+		data->intr_control = fscscy_read_value(client, FSCSCY_REG_INTR_CTRL);
+
+                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. */
+void fscscy_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_REV:
+				results[0] = data->revision ;
+				break;
+			case FSCSCY_SYSCTL_EVENT:
+				results[0] = data->global_event & 0x9f; /* MKN */
+				break;
+			case FSCSCY_SYSCTL_CONTROL:
+				results[0] = data->global_control & 0x19; /* MKN */
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if((ctl_name == FSCSCY_SYSCTL_CONTROL) && (*nrels_mag >= 1)) {
+			data->global_control = (data->global_control & 0x18) | (results[0] & 0x01); /* MKN */
+			printk("fscscy: writing 0x%02x to global_control\n",
+				data->global_control);
+			fscscy_write_value(client,FSCSCY_REG_CONTROL,
+				data->global_control);
+		}
+		else
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+#define TEMP_FROM_REG(val)    (val-128)
+
+
+void fscscy_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_TEMP0:
+				results[0] = data->temp_status[0] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[0]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[0]);
+				results[3] = TEMP_FROM_REG(data->temp_min[0]);
+				results[4] = TEMP_FROM_REG(data->temp_max[0]);
+				break;
+			case FSCSCY_SYSCTL_TEMP1:
+				results[0] = data->temp_status[1] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[1]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[1]);
+				results[3] = TEMP_FROM_REG(data->temp_min[1]);
+				results[4] = TEMP_FROM_REG(data->temp_max[1]);
+				break;
+			case FSCSCY_SYSCTL_TEMP2:
+				results[0] = data->temp_status[2] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[2]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[2]);
+				results[3] = TEMP_FROM_REG(data->temp_min[2]);
+				results[4] = TEMP_FROM_REG(data->temp_max[2]);
+				break;
+			case FSCSCY_SYSCTL_TEMP3:
+				results[0] = data->temp_status[3] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[3]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[3]);
+				results[3] = TEMP_FROM_REG(data->temp_min[3]);
+				results[4] = TEMP_FROM_REG(data->temp_max[3]);
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			switch(ctl_name) {
+				case FSCSCY_SYSCTL_TEMP0:
+					data->temp_status[0] = 
+						(data->temp_status[0] & ~0x02) 
+						| (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x "
+						"to temp0_status\n",
+						data->temp_status[0]);
+					fscscy_write_value(client,
+						FSCSCY_REG_TEMP0_STATE,
+						data->temp_status[0] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP1:
+					data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp1_status\n", data->temp_status[1]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP1_STATE,
+						data->temp_status[1] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP2:
+					data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp2_status\n", data->temp_status[2]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP2_STATE,
+						data->temp_status[2] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP3:
+					data->temp_status[3] = (data->temp_status[3] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp3_status\n", data->temp_status[3]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP3_STATE,
+						data->temp_status[3] & 0x02);
+					break;
+				default:
+					printk("fscscy: ctl_name %d not supported\n",ctl_name);
+			}
+		}
+		else
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+#define VOLT_FROM_REG(val,mult)    (val*mult/255)
+
+void fscscy_volt(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_VOLT0:
+				results[0] = VOLT_FROM_REG(data->volt[0],1420);
+				results[1] = VOLT_FROM_REG(data->volt_min[0],1420);
+				results[2] = VOLT_FROM_REG(data->volt_max[0],1420);
+				break;
+			case FSCSCY_SYSCTL_VOLT1:
+				results[0] = VOLT_FROM_REG(data->volt[1],660);
+				results[1] = VOLT_FROM_REG(data->volt_min[1],660);
+				results[2] = VOLT_FROM_REG(data->volt_max[1],660);
+				break;
+			case FSCSCY_SYSCTL_VOLT2:
+				results[0] = VOLT_FROM_REG(data->volt[2],330);
+				results[1] = VOLT_FROM_REG(data->volt_min[2],330);
+				results[2] = VOLT_FROM_REG(data->volt_max[2],330);
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+void fscscy_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+
+	switch(ctl_name) {
+		case FSCSCY_SYSCTL_FAN0:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				0,FSCSCY_REG_FAN0_STATE,FSCSCY_REG_FAN0_RPMMIN,
+				FSCSCY_REG_FAN0_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN1:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				1,FSCSCY_REG_FAN1_STATE,FSCSCY_REG_FAN1_RPMMIN,
+				FSCSCY_REG_FAN1_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN2:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				2,FSCSCY_REG_FAN2_STATE,FSCSCY_REG_FAN2_RPMMIN,
+				FSCSCY_REG_FAN2_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN3:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				3,FSCSCY_REG_FAN3_STATE,FSCSCY_REG_FAN3_RPMMIN,
+				FSCSCY_REG_FAN3_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN4:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				4,FSCSCY_REG_FAN4_STATE,FSCSCY_REG_FAN4_RPMMIN,
+				FSCSCY_REG_FAN4_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN5:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				5,FSCSCY_REG_FAN5_STATE,FSCSCY_REG_FAN5_RPMMIN,
+				FSCSCY_REG_FAN5_RIPPLE);
+			break;
+		default:
+			printk("fscscy: illegal fan nr %d\n",ctl_name);
+	}
+}
+			
+#define RPM_FROM_REG(val)   (val*60)
+
+void fscscy_fan_internal(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results, int nr,
+	       int reg_state, int reg_min, int reg_ripple )
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->fan_status[nr] & 0x0f; /* MKN */
+		results[1] = data->fan_rpmmin[nr];
+		results[2] = data->fan_ripple[nr] & 0x03;
+		results[3] = RPM_FROM_REG(data->fan_act[nr]);
+		results[4] = RPM_FROM_REG(data->fan_min[nr]);
+		results[5] = RPM_FROM_REG(data->fan_max[nr]);
+		*nrels_mag = 6;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			data->fan_status[nr] = (data->fan_status[nr] & 0x0b) | (results[0] & 0x04); /* MKN */
+			printk("fscscy: writing value 0x%02x to fan%d_status\n",
+				data->fan_status[nr],nr);
+			fscscy_write_value(client,reg_state,
+				data->fan_status[nr]);
+		}
+		if(*nrels_mag >= 2)  {
+			if((results[1] & 0xff) == 0) {
+				 printk("fscscy: fan%d rpmmin 0 not allowed for safety reasons\n",nr);
+				 return;
+			}
+			data->fan_rpmmin[nr] = results[1];
+			printk("fscscy: writing value 0x%02x to fan%d_min\n",
+				data->fan_rpmmin[nr],nr);
+			fscscy_write_value(client,reg_min,
+				data->fan_rpmmin[nr]);
+		}
+		if(*nrels_mag >= 3) {
+			if((results[2] & 0x03) == 0) {
+				printk("fscscy: fan%d ripple 0 is nonsense/not allowed\n",nr);
+				return;
+			}
+			data->fan_ripple[nr] = results[2] & 0x03;
+			printk("fscscy: writing value 0x%02x to fan%d_ripple\n",
+				data->fan_ripple[nr],nr);
+			fscscy_write_value(client,reg_ripple,
+				data->fan_ripple[nr]);
+		}	
+	}
+}
+
+void fscscy_wdog(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->watchdog[0] ;
+		results[1] = data->watchdog[1] & 0x02;
+		results[2] = data->watchdog[2] & 0xb0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->watchdog[0] = results[0] & 0xff;
+			printk("fscscy: writing value 0x%02x to wdog_preset\n",
+				data->watchdog[0]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_PRESET,
+				data->watchdog[0]);
+		} 
+		if (*nrels_mag >= 2) {
+			data->watchdog[1] = results[1] & 0x02;
+			printk("fscscy: writing value 0x%02x to wdog_state\n",
+				data->watchdog[1]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_STATE,
+				data->watchdog[1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->watchdog[2] = results[2] & 0xb0;
+			printk("fscscy: writing value 0x%02x to wdog_control\n",
+				data->watchdog[2]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_CONTROL,
+				data->watchdog[2]);
+		}
+	}
+}
+
+void fscscy_pciload(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->pciload;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscscy: writing PCILOAD to chip not supported\n");
+	}
+}
+
+void fscscy_intrusion(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->intr_control & 0x80;
+		results[1] = data->intr_status & 0xc0;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->intr_control = results[0] & 0x80;
+			printk("fscscy: writing value 0x%02x to intr_control\n",
+				data->intr_control); 
+			fscscy_write_value(client,FSCSCY_REG_INTR_CTRL,
+				data->intr_control);
+		} 
+	}
+}
+
+static int __init sm_fscscy_init(void)
+{
+	printk("fscscy.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&fscscy_driver);
+}
+
+static void __exit sm_fscscy_exit(void)
+{
+	i2c_del_driver(&fscscy_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Martin Knoblauch <mkn@teraport.de> based on work (fscpos) from  Hermann Jung <hej@odn.de>");
+MODULE_DESCRIPTION("fujitsu siemens scylla chip driver");
+
+module_init(sm_fscscy_init);
+module_exit(sm_fscscy_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/ltc1710.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/ltc1710.c	(revision 2867)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/ltc1710.c	(revision 2867)
@@ -0,0 +1,313 @@
+/*
+    ltc1710.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.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.
+*/
+
+/* A few notes about the LTC1710:
+
+* The LTC1710 is a dual programmable switch.  It can be used to turn
+  anything on or off anything which consumes less than 300mA of 
+  current and up to 5.5V
+  
+* The LTC1710 is a very, very simple SMBus device with three possible 
+   SMBus addresses (0x58,0x59, or 0x5A).  Only SMBus byte writes
+   (command writes) are supported.
+
+* Since only writes are supported, READS DON'T WORK!  The device 
+  plays dead in the event of a read, so this makes detection a 
+  bit tricky.
+  
+* BTW- I can safely say that this driver has been tested under
+  every possible case, so there should be no bugs. :')
+  
+  --Phil
+
+*/
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x58, 0x5a, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(ltc1710);
+
+/* The LTC1710 registers */
+
+/* (No registers.  [Wow! This thing is SIMPLE!] ) */
+
+/* Initial values */
+#define LTC1710_INIT 0		/* Both off */
+
+/* Each client has this additional data */
+struct ltc1710_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 status;		/* Register values */
+};
+
+static int ltc1710_attach_adapter(struct i2c_adapter *adapter);
+static int ltc1710_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int ltc1710_detach_client(struct i2c_client *client);
+
+static void ltc1710_switch1(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ltc1710_switch2(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ltc1710_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc1710_driver = {
+	.name		= "LTC1710 sensor chip driver",
+	.id		= I2C_DRIVERID_LTC1710,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ltc1710_attach_adapter,
+	.detach_client	= ltc1710_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LTC1710_SYSCTL_SWITCH_1 1000
+#define LTC1710_SYSCTL_SWITCH_2 1001
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected LTC1710. 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 ltc1710_dir_table_template[] = {
+	{LTC1710_SYSCTL_SWITCH_1, "switch1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ltc1710_switch1},
+	{LTC1710_SYSCTL_SWITCH_2, "switch2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ltc1710_switch2},
+	{0}
+};
+
+static int ltc1710_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, ltc1710_detect);
+}
+
+/* This function is called by i2c_detect */
+int ltc1710_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct ltc1710_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("ltc1710.o: ltc1710_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+		goto ERROR0;
+
+	/* 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 ltc1710_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct ltc1710_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ltc1710_driver;
+	new_client->flags = 0;
+
+	/* Now, we would do the remaining detection. But the LTC1710 is plainly
+	   impossible to detect! Stupid chip. */
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = ltc1710;
+
+	if (kind == ltc1710) {
+		type_name = "ltc1710";
+		client_name = "LTC1710 chip";
+	} else {
+#ifdef DEBUG
+		printk("ltc1710.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					ltc1710_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+
+static int ltc1710_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct ltc1710_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("ltc1710.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+
+}
+
+static void ltc1710_update_client(struct i2c_client *client)
+{
+	struct ltc1710_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting ltc1710 update\n");
+#endif
+
+		/* data->status = i2c_smbus_read_byte(client); 
+		   Unfortunately, reads always fail!  */
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void ltc1710_switch1(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct ltc1710_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ltc1710_update_client(client);
+		results[0] = data->status & 1;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->status = (data->status & 2) | results[0];
+			i2c_smbus_write_byte(client, data->status);
+		}
+	}
+}
+
+void ltc1710_switch2(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct ltc1710_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ltc1710_update_client(client);
+		results[0] = (data->status & 2) >> 1;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->status =
+			    (data->status & 1) | (results[0] << 1);
+			i2c_smbus_write_byte(client, data->status);
+		}
+	}
+}
+
+static int __init sm_ltc1710_init(void)
+{
+	printk("ltc1710.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ltc1710_driver);
+}
+
+static void __exit sm_ltc1710_exit(void)
+{
+	i2c_del_driver(&ltc1710_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LTC1710 driver");
+
+module_init(sm_ltc1710_init);
+module_exit(sm_ltc1710_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca954x.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca954x.c	(revision 2837)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/pca954x.c	(revision 2837)
@@ -0,0 +1,836 @@
+/*
+ * pca954x.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring
+ * This module supports the PCA954x series of I2C multiplexer/switch chips
+ * made by Philips Semiconductors.  This includes the
+ *	PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, and PCA9548.
+ *
+ * Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
+ *
+ * Based on:
+ *    i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
+ * and
+ *    pca9540.c from Jean Delvare <khali@linux-fr.org>, which was
+ *	based on pcf8574.c from the same project by Frodo Looijaard,
+ *	Philip Edelbrock, Dan Eaton and Aurelien Jarno.
+ *
+ * These chips are all controlled via the I2C bus itself, and all have a
+ * single 8-bit register (normally at 0x70).  The upstream "parent" bus fans
+ * out to two, four, or eight downstream busses or channels; which of these
+ * are selected is determined by the chip type and register contents.  A
+ * mux can select only one sub-bus at a time; a switch can select any
+ * combination simultaneously.
+ *
+ * See Documentation/i2c/virtual_i2c for details on the virtual bus/adapter
+ * mechanism.
+ *
+ *********************************************************************
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "i2c-virtual.h"
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+
+#define DEBUG 1		/* XXX: Comment out before release? */
+#ifdef DEBUG
+# define DBG(x) if (pca954x_debug > 0) { x; } else 
+#else
+# define DBG(x)	{}
+#endif
+
+#define PCA954X_NCHANS 8		/* Max # of channels (PCA9548) */
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { /*0x70,*/ SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod/modprobe parameters */
+
+/* Chip type must normally be specified using a parameter of the form
+	"force_pca9544=0,0x70"
+   The following declares the possible types.
+*/
+SENSORS_INSMOD_7(pca9540,pca9542,pca9543,pca9544,pca9545,pca9546,pca9548);
+
+static int pca954x_debug;
+MODULE_PARM(pca954x_debug, "i");
+MODULE_PARM_DESC(pca954x_debug, "Debug output level (0 = none)");
+
+#if 0	/* Considered adding this, but too many issues.  See doc. */
+static int pca954x_selmask[32];	/* Max # chips supported, sigh */
+MODULE_PARM(pca954x_selmask, "1-32i");
+MODULE_PARM_DESC(pca954x_selmask, "List of selmask settings for each mux/swi");
+#endif
+
+/* Provide specs for the PCA954x types we know about
+ */
+static struct pca954x_chipdef {
+        enum chips type;
+        const char *type_name;
+        int pcanum;
+        int nchans;
+        enum muxtype { pca954x_ismux=0, pca954x_isswi } muxtype;
+} pca954x_chipdefs[] = {
+        { pca9540, "pca9540", 9540, 2, pca954x_ismux },
+        { pca9542, "pca9542", 9542, 2, pca954x_ismux },
+        { pca9543, "pca9543", 9543, 2, pca954x_isswi },
+        { pca9544, "pca9544", 9544, 4, pca954x_ismux },
+        { pca9545, "pca9545", 9545, 4, pca954x_isswi },
+        { pca9546, "pca9546", 9546, 4, pca954x_isswi },
+        { pca9548, "pca9548", 9548, 8, pca954x_isswi },
+};
+#define PCA954X_MUX_ENA 0x04	/* Mux enable bit */
+
+/* Each client has this additional data.
+   Note the two conventions for identifying a chip's channel (sub-bus):
+        "channel" 0 to N-1, where N is the number of channels supported
+        "bitmask" 1 to 1 << N-1, so bit 0 is channel 0.
+   This use of "channel" is consistent with that in the Philips PCA954X doc.
+ */
+struct pca954x_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+	enum muxtype muxtype;
+	int pcanum;	/* PCA chip number */
+	int nchans;	/* # of channels (busses) this chip controls */
+	int nchmask;	/* Mask of all channels */
+
+	struct semaphore update_lock;
+	int selmask;	/* Bitmask of chans OK to leave on (chn1 == bit0) */
+	int curmask;	/* Bitmask of chans currently selected */
+
+	u8 regval;	/* Current chip register value */
+	u8 origval;	/* Original value at initialization */
+
+	struct i2c_adapter *virt_adapters[PCA954X_NCHANS];
+};
+
+static int  pca954x_attach_adapter(struct i2c_adapter *adapter);
+static int  pca954x_detect(struct i2c_adapter *adapter, int address,
+                           unsigned short flags, int kind);
+static int  pca954x_detach_client(struct i2c_client *client);
+static void pca954x_pe_busses(struct i2c_client *client, int operation,
+                              int ctl_name, int *nrels_mag, long *results);
+static void pca954x_pe_debug(struct i2c_client *client, int operation,
+                             int ctl_name, int *nrels_mag, long *results);
+static void pca954x_pe_regval(struct i2c_client *client, int operation,
+                              int ctl_name, int *nrels_mag, long *results);
+static void pca954x_pe_selbus(struct i2c_client *client, int operation,
+                              int ctl_name, int *nrels_mag, long *results);
+static void pca954x_pe_selmask(struct i2c_client *client, int operation,
+                               int ctl_name, int *nrels_mag, long *results);
+static void pca954x_pe_type(struct i2c_client *client, int operation,
+                            int ctl_name, int *nrels_mag, long *results);
+static void pca954x_set_selmask(struct i2c_client *client, int mask);
+static u8   pca954x_mask2val(struct i2c_client *client, int mask);
+static int  pca954x_val2mask(struct i2c_client *client, u8 val);
+
+static int  pca954x_select_mux(struct i2c_adapter *adap,
+                               struct i2c_mux_ctrl *mux);
+static int  pca954x_deselect_mux(struct i2c_adapter *adap,
+                                 struct i2c_mux_ctrl *mux);
+static int  pca954x_select_chan(struct i2c_adapter *adap,
+                                struct i2c_client *client, unsigned long chan);
+static int pca954x_xfer(struct i2c_adapter *adap,
+                        struct i2c_client *client,
+                        int read_write, u8 *val);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pca954x_driver = {
+	.name		= "PCA954X I2C mux/switch driver",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= pca954x_attach_adapter,
+	.detach_client	= pca954x_detach_client,
+};
+
+
+/* -- SENSORS SYSCTL START -- */
+
+#define PCA954X_SYSCTL_BUSSES		1000
+#define PCA954X_SYSCTL_DEBUG		1001
+#define PCA954X_SYSCTL_REGVAL		1002
+#define PCA954X_SYSCTL_SELBUS		1003
+#define PCA954X_SYSCTL_SELMASK		1004
+#define PCA954X_SYSCTL_TYPE		1005
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table pca954x_dir_table_template[] = {
+	{PCA954X_SYSCTL_BUSSES, "busses", NULL, 0, 0444, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_busses},
+	{PCA954X_SYSCTL_DEBUG, "debug", NULL, 0, 0644, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_debug},
+	{PCA954X_SYSCTL_REGVAL, "regval", NULL, 0, 0444, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_regval},
+	{PCA954X_SYSCTL_SELBUS, "selbus", NULL, 0, 0644, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selbus},
+	{PCA954X_SYSCTL_SELMASK, "selmask", NULL, 0, 0644, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selmask},
+	{PCA954X_SYSCTL_TYPE, "type", NULL, 0, 0444, NULL,
+         &i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_type},
+	{0}
+};
+
+static int pca954x_id = 0;
+
+static int __init pca954x_init(void)
+{
+	printk("pca954x.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+        /* Because we've set I2C_DF_NOTIFY in our driver struct,
+         * this call results in calling pca954x_attach_adapter() for
+         * every adapter currently known, both real and virtual.
+         */
+	return i2c_add_driver(&pca954x_driver);
+}
+
+static void __exit pca954x_exit(void)
+{
+	i2c_del_driver(&pca954x_driver);
+}
+
+/* This function is called whenever a new bus/adapter is added, OR when
+   the driver itself is added (for all existing adapters), to see if
+   the driver can find a chip address it's responsible for. 
+ */
+static int pca954x_attach_adapter(struct i2c_adapter *adapter)
+{
+	DBG(printk(KERN_DEBUG "%s: %0lX\n",
+               __FUNCTION__, (unsigned long)adapter));
+
+	/* Apply standard lookup via parameters or probing.
+           "addr_data" is defined by the SENSORS_INSMOD_n macro.
+         */
+	return i2c_detect(adapter, &addr_data, pca954x_detect);
+}
+
+/* This function is called by i2c_detect and must be of type
+   "i2c_found_addr_proc" as defined in i2c-proc.h.
+   For the PCA954x series this should only happen due to "force" parameters
+   given to insmod/modprobe, as there is no way to autodetect what flavor
+   of chip we have (and the differences matter!)
+
+   Thus, we should never get a "kind" of -1.
+
+   Note the unique potential for infinite recursion here because we are
+   creating new adapters (one for each muxed virtual bus).  This will cause
+   our attach_adapter() function to be called twice for each new adapter,
+   because:
+
+   (1) The call to i2c_add_adapter results in calling the attach_adapter()
+	function of all known chip drivers, including this one.
+
+   (2) Because normally this is first called via i2c_add_driver calling
+	i2c_add_adapter, each new virtual bus/adapter created here will create
+        a new entry in i2c-core's master adapter list at a point that hasn't
+        yet been reached by i2c_add_driver's scan, and thus i2c_add_driver
+        will cause yet another call to our attach_adapter for each of these
+        new bus/adapters.
+
+   Each call to our attach_adapter() causes another call to i2c_detect,
+   which would detect the mux all over again if an actual probe was made.
+
+   This is prevented in two ways.  The first line of defense is
+   i2c_check_addr() which is called by both i2c-proc.c:i2c_detect()
+   and i2c-core.c:i2c_probe() to make sure a bus/addr pair has not already
+   been seen; that function now knows how to verify this on all parent
+   busses as well.
+
+   The second line of defense is to never put anything in the "normal_i2c"
+   or "normal_i2c_range" tables that would induce i2c_detect() to probe
+   a PCA954x address.
+ */
+int pca954x_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i, n;
+	struct i2c_client *client;
+	struct pca954x_data *data;
+	const char *type_name = "";
+	int err = 0;
+
+	DBG(printk(KERN_DEBUG "%s: %0lX %0x %0x %d\n",
+               __FUNCTION__, (unsigned long)adapter,
+               address, flags, kind));
+
+        /* XXX: Remove this after verifying that no more recursion
+           is happening!
+         */
+        if (adapter->algo->id == I2C_ALGO_VIRT) {
+
+            printk(KERN_ERR "%s: Avoiding recursion! %0lX %0x %0x %d\n",
+                   __FUNCTION__, (unsigned long)adapter,
+                   address, flags, kind);
+            goto ERROR0;
+        }
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet. */
+	if (!(data = kmalloc(sizeof(struct pca954x_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+        memset(data, 0, sizeof(struct pca954x_data));
+
+	client = &data->client;
+	client->addr = address;
+	client->data = data;
+	client->adapter = adapter;
+	client->driver = &pca954x_driver;
+	client->flags = 0;
+
+	/* The detection is very weak.  In fact, we have no way of
+         * telling what kind of mux/swi is there, and the differences matter,
+         * so if asked to figure it out we just complain instead.
+         */
+	if (kind < 0) {
+		printk(KERN_ERR "%s: Attempted ill-advised probe at addr %0x",
+                       __FUNCTION__, address);
+		goto ERROR1;
+	}
+
+        /* Read the mux register at addr.  This does two things: it verifies
+           that the mux is in fact present, and fetches its current
+           contents for possible use with a future deselect algorithm.
+        */
+        if ((i = i2c_smbus_read_byte(client)) < 0) {
+		printk(KERN_WARNING
+                       "i2c-%d: pca954x.o failed to read reg at %0x",
+                       i2c_adapter_id(adapter), address);
+                goto ERROR1;
+        }
+        data->origval = i;
+
+        if (kind == any_chip) {
+		printk(KERN_WARNING
+                       "i2c-%d: pca954x.o needs advice on chip type -"
+                       " wildly guessing %0x is a PCA9540",
+                       i2c_adapter_id(adapter), address);
+                kind = pca9540;	/* Make "any" default to PCA9540 */
+        }
+
+        /* Look up in table */
+        for (i = sizeof(pca954x_chipdefs)/sizeof(pca954x_chipdefs[0]);
+             --i >= 0;) {
+            if (pca954x_chipdefs[i].type == kind)
+                break;
+        }
+        if (i < 0) {
+		printk(KERN_ERR "%s: Internal error: unknown kind (%d)",
+                       __FUNCTION__, kind);
+		goto ERROR1;
+        }
+        data->type = kind;
+        type_name     = pca954x_chipdefs[i].type_name;
+        data->pcanum  = pca954x_chipdefs[i].pcanum;
+        data->nchans  = pca954x_chipdefs[i].nchans;
+        data->muxtype = pca954x_chipdefs[i].muxtype;
+        data->nchmask = (1 << data->nchans) - 1;
+
+        /* Now that we know the mux/swi type, we can analyze the
+           pca954x_selmask parameter, if one was specified.
+
+           XXX: Not yet.  Still uncertain whether there is a good way
+           to initialize selmask and still continue to detect other chips
+           correctly on the right busses, or whether the original reg
+           setting (origval) should be retained in case BIOS depends on it.
+         */
+
+	/* Fill in remaining client fields and put it into the global list */
+	snprintf(client->name, sizeof(client->name),
+                 "PCA%d I2C %d-chan %s chip",
+                 data->pcanum, data->nchans,
+                 (data->muxtype == pca954x_ismux ? "mux" : "switch"));
+	client->id = pca954x_id++;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(client)))
+		goto ERROR1;
+
+	/* Register new /proc directory entries with module sensors */
+	if ((i = i2c_register_entry(client, type_name,
+				    pca954x_dir_table_template,
+				    THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the PCA954X chip.
+           For now, force unselected.  This had better happen BEFORE any
+           other sensors modules are loaded, or their chip addresses will
+           stop working if they were actually behind the mux chip.
+         */
+        pca954x_set_selmask(client, 0);
+
+	/* Now create virtual busses and adapters for them */
+
+#ifdef I2C_REQUIRE_ARBITRATION
+        /* This doesn't actually do anything yet; kept as a
+           placeholder from the original i2c-virtual_cb.c
+         */
+	adapter->algo->acquire_exclusive = &acquire_shared_bus;
+	adapter->algo->release_exclusive = &release_shared_bus;
+#endif
+
+	DBG(printk(KERN_DEBUG "%s: creating %d virt busses\n",
+               __FUNCTION__, data->nchans));
+
+        for (i = 0, n = 0; i < data->nchans; ++i) {
+            	struct i2c_adapter *virt;
+                virt = i2c_virt_create_adapter(adapter, client, i,
+                                               &pca954x_select_mux,
+                                               &pca954x_deselect_mux);
+                if ((data->virt_adapters[i] = virt) != NULL)
+                    ++n;
+        }
+	printk("i2c-%d: Registered %d of %d virtual busses for I2C mux %s\n",
+               i2c_adapter_id(adapter), n, i, type_name);
+
+	return 0;
+
+      ERROR2:
+	i2c_detach_client(client);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int pca954x_detach_client(struct i2c_client *client)
+{
+        struct pca954x_data *data = (struct pca954x_data *)(client->data);
+	int i, err;
+
+	DBG(printk(KERN_DEBUG "%s: %0lX\n",
+               __FUNCTION__, (unsigned long)client));
+
+        /* Must flush everything attached to our channels!
+         */
+        for (i = 0; i < data->nchans; ++i) {
+            if (data->virt_adapters[i]) {
+                err = i2c_virt_remove_adapter(data->virt_adapters[i]);
+                if (err != 0) {
+                    printk(KERN_ERR "pca954x.o: Bus deregistration failed, "
+                           "client not detached.\n");
+                    return err;
+                }
+                data->virt_adapters[i] = NULL;
+            }
+        }
+
+	i2c_deregister_entry(data->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "pca954x.o: Client deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* Implement /proc "regval" - the actual contents of the chip register.
+ * This is READ-ONLY.
+ */
+void pca954x_pe_regval(struct i2c_client *client, int operation,
+                       int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca954x_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+#if 0	/* XXX: For now, always use cached value */
+		static void pca954x_update_client(struct i2c_client *client);
+		pca954x_update_client(client);
+#endif
+                results[0] = data->regval;
+		*nrels_mag = 1;
+	}
+}
+
+/* Implement /proc "debug" - a non-zero value enables debug output, if
+ * the code was compiled with it.  This actually applies to all mux chips,
+ * not just a single instance, but that's OK.
+ */
+void pca954x_pe_debug(struct i2c_client *client, int operation,
+                      int ctl_name, int *nrels_mag, long *results)
+{
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = pca954x_debug;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			pca954x_debug = results[0];
+		}
+	}
+}
+
+/* Implement /proc "busses" - list of (virtual) adapter IDs that constitute
+   the busses that this chip connects to.
+   This is READ-ONLY.
+ */
+void pca954x_pe_busses(struct i2c_client *client, int operation,
+                       int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca954x_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		int i, lasti;
+                if ((lasti = data->nchans) > *nrels_mag)
+			lasti = *nrels_mag;
+                for (i = 0; i < lasti; ++i) {
+                        results[i] = i2c_adapter_id(data->virt_adapters[i]);
+                }
+		*nrels_mag = lasti;
+	}
+}
+
+/* Implement /proc "selmask" - the value specifies the current default
+ * channel mask.  Sub-bus 0 == bit 0.
+ # A value of 0 means "none"
+ */
+void pca954x_pe_selmask(struct i2c_client *client, int operation,
+                        int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca954x_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->selmask;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+                        pca954x_set_selmask(client, results[0]);
+		}
+	}
+}
+
+
+/* Implement /proc "selbus" - the list of values is considered to be virtual
+   adapter IDs, as an alternative representation of the select mask.
+   No values at all means "none".
+ */
+void pca954x_pe_selbus(struct i2c_client *client, int operation,
+                       int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca954x_data *data = client->data;
+        int i, nres;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+                for (i = 0, nres = 0; i < data->nchans; ++i) {
+                    if ((data->selmask & (1<<i)) && (nres < *nrels_mag)) {
+                        results[nres] = i2c_adapter_id(data->virt_adapters[i]);
+                        ++nres;
+                    }
+                }
+		*nrels_mag = nres;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		int newmask = 0;
+		for (nres = 0; nres < *nrels_mag; ++nres) {
+                    /* Ugly, but shouldn't happen very often */
+                    for (i = 0; i < data->nchans; ++i) {
+                        if (results[nres]
+                            == i2c_adapter_id(data->virt_adapters[i])) {
+				newmask |= (1<<i);
+                        }
+                    }
+		}
+                pca954x_set_selmask(client, newmask);
+	}
+}
+
+
+/* Implement /proc "type" - expose details of the mux type.
+   Due to limitations of the sensor package conventions, we can only provide
+   an integer array rather than strings:
+	[0] = chip number: 9540 for "PCA9540", etc.
+        [1] = mux type: 0 for mux, 1 for switch
+        [2] = # channels: 2, 4, or 8
+ */
+void pca954x_pe_type(struct i2c_client *client, int operation,
+                        int ctl_name, int *nrels_mag, long *results)
+{
+	struct pca954x_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (*nrels_mag >= 3) {
+			results[0] = data->pcanum;
+			results[1] = data->muxtype;
+			results[2] = data->nchans;
+                        *nrels_mag = 3;
+                }
+	}
+}
+
+
+/* Set default select mask.
+   Since this can be executed while something else is using the mux or
+   one of its sub-busses, the main consideration is making sure we don't
+   mess up anything in progress.
+
+   This is accomplished by locking the parent bus that the mux resides on,
+   and then calling our internal write function.
+
+   It would not work to just call i2c_smbus_write_byte() because
+   by the time that returns, the values we're trying to update
+   could already be wrong.  This can happen if something else grabs
+   the bus and writes to the mux (eg for doing I/O to a sub-bus
+   device), leading to a race condition for updating
+   selmask, regval, and curmask.
+
+   It doesn't help to use an update_lock for those values because
+   you still have to contend with other threads trying to select
+   sub-busses and would encounter deadlocks.
+
+ */
+static void pca954x_set_selmask(struct i2c_client *client, int mask)
+{
+	struct pca954x_data *data = client->data;
+
+	DBG(printk(KERN_DEBUG
+                   "%s: Locking bus %d mux %d (%0lX) "
+                   "for write of selmask %0x\n",
+                   __FUNCTION__, i2c_adapter_id(client->adapter),
+                   client->id, (unsigned long)client, mask));
+
+	down(&client->adapter->bus);
+	DBG(printk(KERN_DEBUG "%s: Starting write.\n", __FUNCTION__));
+	data->selmask = (mask & data->nchmask);
+        (void) pca954x_select_chan(client->adapter, client, -1);
+	up(&client->adapter->bus);
+
+	DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for write.\n",
+                   __FUNCTION__, (unsigned long)client));
+}
+
+#if 0 /* Not needed at the moment; see pca954x_pe_regval() */
+
+/* Read pca954x register into cached value
+ */
+static void pca954x_update_client(struct i2c_client *client)
+{
+	struct pca954x_data *data = client->data;
+
+	DBG(printk(KERN_DEBUG
+                   "%s: Locking bus %d mux %d (%0lX) for read\n",
+                   __FUNCTION__, i2c_adapter_id(client->adapter),
+                   client->id, (unsigned long)client));
+
+	down(&client->adapter->bus);
+	DBG(printk(KERN_DEBUG "%s: Starting read.\n", __FUNCTION__));
+        pca954x_xfer(client->adapter, client, I2C_SMBUS_READ, &data->regval);
+        data->curmask = pca954x_val2mask(client, data->regval);
+	up(&client->adapter->bus);
+
+	DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for read.\n",
+                   __FUNCTION__, (unsigned long)client));
+}
+#endif
+
+
+static u8 pca954x_mask2val(struct i2c_client *client, int mask)
+{
+	struct pca954x_data *data = (struct pca954x_data *)(client->data);
+        int i;
+
+        mask &= data->nchmask;		/* Sanitize mask */
+        if (mask == 0)
+		return 0;		/* Deselecting all, turn off */
+        if (data->muxtype == pca954x_isswi)
+		return mask;		/* Switch, can enable all at once! */
+
+        /* Mux, can only pick one.  Select lowest chan bit */
+        for (i=0; (mask & (1<<i)) == 0; ++i);
+        return PCA954X_MUX_ENA | i;
+}
+
+static int pca954x_val2mask(struct i2c_client *client, u8 val)
+{
+	struct pca954x_data *data = (struct pca954x_data *)(client->data);
+
+        if (data->muxtype == pca954x_isswi)
+		return val & data->nchmask;	/* Switch val is == mask */
+
+        /* Mux - mask off low bits to get 0-based chan # as bit shift */
+        if (val & PCA954X_MUX_ENA)
+                return 1 << (val & (data->nchans-1));
+        return 0;
+}
+
+/*****************************************************************************
+ * All of the following functions should only be called after the parent
+ * adapter bus semaphore (adapter->bus) has been acquired, so we cannot
+ * call the high-level i2c_smbus_write_byte() function -- it will deadlock
+ * attempting to acquire the same bus lock.  Instead do directly what it
+ * would do, by means of pca954x_xfer().  Sigh!
+ *
+ * These functions are allowed to block.
+ * 
+ *****************************************************************************/
+
+
+static int pca954x_select_mux(struct i2c_adapter *adap,
+                              struct i2c_mux_ctrl *mux)
+{
+        return pca954x_select_chan(adap, mux->client, mux->value);
+}
+
+static int pca954x_deselect_mux(struct i2c_adapter *adap,
+                                struct i2c_mux_ctrl *mux) 
+{
+        return pca954x_select_chan(adap, mux->client, -1);
+}
+
+/* Select channel.
+ * PCA954x I2C-controlled bus mux/switch chips mainly differ in
+ *	whether the chip is a mux or a switch:
+ *	Mux: value addressed, only 1 bus can be selected.
+ *	Swi: bitmask selected, any/all busses can be selected.
+ */
+static int pca954x_select_chan(struct i2c_adapter *adap,
+                               struct i2c_client *client,
+                               unsigned long chan)
+{
+	struct pca954x_data *data = (struct pca954x_data *)(client->data);
+        int maskbit;
+        u8 newregval;
+        int ret;
+
+        if (chan == -1) {		/* Op: clever deselect (to selmask) */
+            if ((data->muxtype == pca954x_ismux)
+                && (data->curmask & data->selmask)) {
+                /* If mux, and current chan allowed by selmask, do nothing */
+                newregval = data->regval;	/* No change */
+            } else {
+                /* Anything else just reverts to selmask */
+                newregval = pca954x_mask2val(client, data->selmask);
+            }
+
+        } else {			/* Op: select chan */
+            maskbit = 1 << chan;
+            if (maskbit & data->curmask) {	/* If already selected, */
+                newregval = data->regval;	/* no change */
+            } else if ((data->muxtype == pca954x_ismux)
+                       || !(maskbit & data->selmask)) {
+                newregval = pca954x_mask2val(client, maskbit);
+            } else
+                newregval = pca954x_mask2val(client, data->selmask);
+        }
+
+        /* Only clobber control reg when value changes, to avoid
+           unnecessary mux writes.  For safety and initialization,
+           ALWAYS force the write if 0 is desired.
+        */
+        ret = (!newregval || (data->regval != newregval));
+        DBG(printk(KERN_DEBUG
+                   "%s: Mux %d: chan %d, val %02x - %s\n",
+                   __FUNCTION__,
+                   client->id, (int)chan, newregval,
+                   (ret ? "writing" : "skipping" ) ));
+        if (ret) {
+		ret = pca954x_xfer(adap, client, I2C_SMBUS_WRITE, &newregval);
+
+                if (ret < 0) {
+                    printk(KERN_ERR "%s: I2C mux %d select failed "
+                           "(addr=%02x, val=%02x, err=%d)!\n", 
+                           __FUNCTION__, client->id,
+                           client->addr, newregval, ret);
+                } else {
+                    data->regval = newregval;
+                    data->curmask = pca954x_val2mask(client, newregval);
+                }
+        }
+        return ret;
+}
+
+
+static int pca954x_xfer(struct i2c_adapter *adap,
+                        struct i2c_client *client,
+                        int read_write, u8 *val)
+{
+	int ret;
+
+	if (adap->algo->master_xfer) {
+		/* Use I2C transfer */
+                struct i2c_msg msg;
+                char buf[1];
+
+                msg.addr  = client->addr;
+                msg.flags = (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0);
+                msg.len	  = 1;
+                buf[0] 	  = *val;
+                msg.buf   = buf;
+                ret = adap->algo->master_xfer(adap, &msg, 1);
+                if (!ret && (read_write == I2C_SMBUS_READ))
+			*val = buf[0];
+
+        } else if (adap->algo->smbus_xfer) {
+		/* Use SMBus transfer */
+                union i2c_smbus_data data;
+                ret = adap->algo->smbus_xfer(adap,
+                                             client->addr,
+                                             client->flags & I2C_M_TEN,
+                                             read_write,
+                                             *val,
+                                             I2C_SMBUS_BYTE, &data);
+                if (!ret && (read_write == I2C_SMBUS_READ))
+			*val = data.byte;
+        } else {
+            printk(KERN_ERR "%s: no smbus_xfer or master_xfer for "
+                   "adapter %0lX, client %0lX\n",
+                   __FUNCTION__, (unsigned long)adap, (unsigned long)client);
+            ret = -1;
+        }
+        return ret;
+}
+
+
+/* ------------------ */
+
+MODULE_AUTHOR("Ken Harrenstien <klh@google.com>");
+MODULE_DESCRIPTION("PCA954X I2C mux/switch driver");
+
+module_init(pca954x_init);
+module_exit(pca954x_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscpos.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscpos.c	(revision 3156)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/fscpos.c	(revision 3156)
@@ -0,0 +1,685 @@
+/*
+    fscpos.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2001 Hermann Jung <hej@odn.de>
+
+    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.
+*/
+
+/* 
+    fujitsu siemens poseidon chip, 
+    module based on lm80.c 
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include "version.h"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(fscpos);
+
+/* The FSCPOS registers */
+
+/* chip identification */
+#define FSCPOS_REG_IDENT_0    0x00
+#define FSCPOS_REG_IDENT_1    0x01
+#define FSCPOS_REG_IDENT_2    0x02
+#define FSCPOS_REG_REVISION   0x03
+
+/* global control and status */
+#define FSCPOS_REG_EVENT_STATE  0x04
+#define FSCPOS_REG_CONTROL       0x05
+
+/* watchdog */
+#define FSCPOS_REG_WDOG_PRESET      0x28
+#define FSCPOS_REG_WDOG_STATE       0x23
+#define FSCPOS_REG_WDOG_CONTROL     0x21
+
+/* fan 0  */
+#define FSCPOS_REG_FAN0_MIN      0x55
+#define FSCPOS_REG_FAN0_ACT      0x0e
+#define FSCPOS_REG_FAN0_STATE   0x0d
+#define FSCPOS_REG_FAN0_RIPPLE   0x0f
+
+/* fan 1  */
+#define FSCPOS_REG_FAN1_MIN      0x65
+#define FSCPOS_REG_FAN1_ACT      0x6b
+#define FSCPOS_REG_FAN1_STATE   0x62
+#define FSCPOS_REG_FAN1_RIPPLE   0x6f
+
+/* fan 2  */
+/* min speed fan2 not supported */
+#define FSCPOS_REG_FAN2_ACT      0xab
+#define FSCPOS_REG_FAN2_STATE   0xa2
+#define FSCPOS_REG_FAN2_RIPPLE   0x0af
+
+/* voltage supervision */
+#define FSCPOS_REG_VOLT_12       0x45
+#define FSCPOS_REG_VOLT_5        0x42
+#define FSCPOS_REG_VOLT_BATT     0x48
+
+/* temperatures */
+/* sensor 0 */
+#define FSCPOS_REG_TEMP0_ACT       0x64
+#define FSCPOS_REG_TEMP0_STATE    0x71
+
+/* sensor 1 */
+#define FSCPOS_REG_TEMP1_ACT       0x32
+#define FSCPOS_REG_TEMP1_STATE    0x81
+
+/* sensor 2 */
+#define FSCPOS_REG_TEMP2_ACT       0x35
+#define FSCPOS_REG_TEMP2_STATE    0x91
+
+
+
+
+/* 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,nr) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+/* Initial limits */
+
+/* For each registered FSCPOS, we need to keep some data in memory. That
+   data is pointed to by fscpos_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new fscpos client is
+   allocated. */
+struct fscpos_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  revision;        /* revision of chip */
+	u8  global_event;    /* global event status */
+	u8  global_control;  /* global control register */
+	u8  watchdog[3];     /* watchdog */
+	u8  volt[3];         /* 12, 5, battery current */ 
+	u8  temp_act[3];     /* temperature */
+	u8  temp_status[3];  /* status of sensor */
+	u8  fan_act[3];      /* fans revolutions per second */
+	u8  fan_status[3];   /* fan status */
+	u8  fan_min[3];      /* fan min value for rps */
+	u8  fan_ripple[3];   /* divider for rps */
+};
+
+
+static int fscpos_attach_adapter(struct i2c_adapter *adapter);
+static int fscpos_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int fscpos_detach_client(struct i2c_client *client);
+
+static int fscpos_read_value(struct i2c_client *client, u8 register);
+static int fscpos_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void fscpos_update_client(struct i2c_client *client);
+static void fscpos_init_client(struct i2c_client *client);
+
+
+static void fscpos_in(struct i2c_client *client, int operation, int ctl_name,
+		    	int *nrels_mag, long *results);
+static void fscpos_fan(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_fan_internal(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results, 
+		     	int nr, int reg_state, int reg_min, int res_ripple);
+static void fscpos_temp(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_volt(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_wdog(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver fscpos_driver = {
+	.name		= "FSCPOS sensor driver",
+	.id		= I2C_DRIVERID_FSCPOS,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= fscpos_attach_adapter,
+	.detach_client	= fscpos_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define FSCPOS_SYSCTL_VOLT0    1000       /* 12 volt supply */
+#define FSCPOS_SYSCTL_VOLT1    1001       /* 5 volt supply */
+#define FSCPOS_SYSCTL_VOLT2    1002       /* batterie voltage*/
+#define FSCPOS_SYSCTL_FAN0     1101       /* state, min, ripple, actual value fan 0 */
+#define FSCPOS_SYSCTL_FAN1     1102       /* state, min, ripple, actual value fan 1 */
+#define FSCPOS_SYSCTL_FAN2     1103       /* state, min, ripple, actual value fan 2 */
+#define FSCPOS_SYSCTL_TEMP0    1201       /* state and value of sensor 0, cpu die */
+#define FSCPOS_SYSCTL_TEMP1    1202       /* state and value of sensor 1, motherboard */
+#define FSCPOS_SYSCTL_TEMP2    1203       /* state and value of sensor 2, chassis */
+#define FSCPOS_SYSCTL_REV     2000        /* Revision */
+#define FSCPOS_SYSCTL_EVENT   2001        /* global event status */
+#define FSCPOS_SYSCTL_CONTROL 2002        /* global control byte */
+#define FSCPOS_SYSCTL_WDOG     2003       /* state, min, ripple, actual value fan 2 */
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected FSCPOS. 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 fscpos_dir_table_template[] = {
+	{FSCPOS_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_wdog},
+	{0}
+};
+
+static int fscpos_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, fscpos_detect);
+}
+
+static int fscpos_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct fscpos_data *data;
+	int err = 0;
+	const char *type_name, *client_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
+		    ("fscpos.o: fscpos_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* 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 fscpos_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &fscpos_driver;
+	new_client->flags = 0;
+
+	/* Do the remaining detection unless force or force_fscpos parameter */
+	if (kind < 0) {
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) != 0x50)
+			goto ERROR1;
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) != 0x45)
+			goto ERROR1;
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) != 0x47)
+			goto ERROR1;
+	}
+
+	kind = fscpos;
+
+	type_name = "fscpos";
+	client_name = "fsc poseidon chip";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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(new_client, type_name,
+					fscpos_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	fscpos_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:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int fscpos_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct fscpos_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("fscpos.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int fscpos_read_value(struct i2c_client *client, u8 reg)
+{
+#ifdef DEBUG
+	printk("fscpos: read reg 0x%02x\n",reg);
+#endif
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+#ifdef DEBUG
+	printk("fscpos: write reg 0x%02x, val 0x%02x\n",reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCPOS. */
+static void fscpos_init_client(struct i2c_client *client)
+{
+	struct fscpos_data *data = client->data;
+
+	/* read revision from chip */
+	data->revision =  fscpos_read_value(client,FSCPOS_REG_REVISION);
+	/* setup missing fan2_min value */
+	data->fan_min[2] = 0xff;
+}
+
+static void fscpos_update_client(struct i2c_client *client)
+{
+	struct fscpos_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting fscpos update\n");
+#endif
+		data->temp_act[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_ACT);
+		data->temp_act[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_ACT);
+		data->temp_act[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_ACT);
+		data->temp_status[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_STATE);
+		data->temp_status[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_STATE);
+		data->temp_status[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_STATE);
+
+		data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12);
+		data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5);
+		data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT);
+
+		data->fan_act[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_ACT);
+		data->fan_act[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_ACT);
+		data->fan_act[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_ACT);
+		data->fan_status[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_STATE);
+		data->fan_status[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_STATE);
+		data->fan_status[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_STATE);
+		data->fan_min[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_MIN);
+		data->fan_min[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_MIN);
+		/* fan2_min is not supported */
+		data->fan_ripple[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_RIPPLE);
+		data->fan_ripple[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_RIPPLE);
+		data->fan_ripple[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_RIPPLE);
+
+		data->watchdog[0] = fscpos_read_value(client, FSCPOS_REG_WDOG_PRESET);
+		data->watchdog[1] = fscpos_read_value(client, FSCPOS_REG_WDOG_STATE);
+		data->watchdog[2] = fscpos_read_value(client, FSCPOS_REG_WDOG_CONTROL);
+
+		data->global_event = fscpos_read_value(client, FSCPOS_REG_EVENT_STATE);
+
+                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. */
+void fscpos_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_REV:
+				results[0] = data->revision ;
+				break;
+			case FSCPOS_SYSCTL_EVENT:
+				results[0] = data->global_event & 0x1f;
+				break;
+			case FSCPOS_SYSCTL_CONTROL:
+				results[0] = data->global_control & 0x01;
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if((ctl_name == FSCPOS_SYSCTL_CONTROL) && (*nrels_mag >= 1)) {
+			data->global_control = (results[0] & 0x01);
+			printk("fscpos: writing 0x%02x to global_control\n",
+				data->global_control);
+			fscpos_write_value(client,FSCPOS_REG_CONTROL,
+				data->global_control);
+		}
+		else
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+#define TEMP_FROM_REG(val)    (val-128)
+
+
+void fscpos_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_TEMP0:
+				results[0] = data->temp_status[0] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[0]);
+				break;
+			case FSCPOS_SYSCTL_TEMP1:
+				results[0] = data->temp_status[1] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[1]);
+				break;
+			case FSCPOS_SYSCTL_TEMP2:
+				results[0] = data->temp_status[2] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[2]);
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			switch(ctl_name) {
+				case FSCPOS_SYSCTL_TEMP0:
+					data->temp_status[0] = 
+						(data->temp_status[0] & ~0x02) 
+						| (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x "
+						"to temp0_status\n",
+						data->temp_status[0]);
+					fscpos_write_value(client,
+						FSCPOS_REG_TEMP0_STATE,
+						data->temp_status[0] & 0x02);
+					break;
+				case FSCPOS_SYSCTL_TEMP1:
+					data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x to temp1_status\n", data->temp_status[1]);
+					fscpos_write_value(client,FSCPOS_REG_TEMP1_STATE,
+						data->temp_status[1] & 0x02);
+					break;
+				case FSCPOS_SYSCTL_TEMP2:
+					data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x to temp2_status\n", data->temp_status[2]);
+					fscpos_write_value(client,FSCPOS_REG_TEMP2_STATE,
+						data->temp_status[2] & 0x02);
+					break;
+				default:
+					printk("fscpos: ctl_name %d not supported\n",ctl_name);
+			}
+		}
+		else
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+#define VOLT_FROM_REG(val,mult)    (val*mult/255)
+
+void fscpos_volt(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_VOLT0:
+				results[0] = VOLT_FROM_REG(data->volt[0],1420);
+				break;
+			case FSCPOS_SYSCTL_VOLT1:
+				results[0] = VOLT_FROM_REG(data->volt[1],660);
+				break;
+			case FSCPOS_SYSCTL_VOLT2:
+				results[0] = VOLT_FROM_REG(data->volt[2],330);
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+void fscpos_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+
+	switch(ctl_name) {
+		case FSCPOS_SYSCTL_FAN0:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				0,FSCPOS_REG_FAN0_STATE,FSCPOS_REG_FAN0_MIN,
+				FSCPOS_REG_FAN0_RIPPLE);
+			break;
+		case FSCPOS_SYSCTL_FAN1:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				1,FSCPOS_REG_FAN1_STATE,FSCPOS_REG_FAN1_MIN,
+				FSCPOS_REG_FAN1_RIPPLE);
+			break;
+		case FSCPOS_SYSCTL_FAN2:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				2,FSCPOS_REG_FAN2_STATE,0xff,
+				FSCPOS_REG_FAN2_RIPPLE);
+			break;
+		default:
+			printk("fscpos: illegal fan nr %d\n",ctl_name);
+	}
+}
+			
+#define RPM_FROM_REG(val)   (val*60)
+
+void fscpos_fan_internal(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results, int nr,
+	       int reg_state, int reg_min, int reg_ripple )
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		results[0] = data->fan_status[nr] & 0x04;
+		results[1] = data->fan_min[nr];
+		results[2] = data->fan_ripple[nr] & 0x03;
+		results[3] = RPM_FROM_REG(data->fan_act[nr]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			data->fan_status[nr] = results[0] & 0x04;
+			printk("fscpos: writing value 0x%02x to fan%d_status\n",
+				data->fan_status[nr],nr);
+			fscpos_write_value(client,reg_state,
+				data->fan_status[nr]);
+		}
+		if((*nrels_mag >= 2) && (nr < 2)) {  
+			/* minimal speed for fan2 not supported */
+			data->fan_min[nr] = results[1];
+			printk("fscpos: writing value 0x%02x to fan%d_min\n",
+				data->fan_min[nr],nr);
+			fscpos_write_value(client,reg_min,
+				data->fan_min[nr]);
+		}
+		if(*nrels_mag >= 3) {
+			if((results[2] & 0x03) == 0) {
+				printk("fscpos: fan%d ripple 0 not allowed\n",nr);
+				return;
+			}
+			data->fan_ripple[nr] = results[2] & 0x03;
+			printk("fscpos: writing value 0x%02x to fan%d_ripple\n",
+				data->fan_ripple[nr],nr);
+			fscpos_write_value(client,reg_ripple,
+				data->fan_ripple[nr]);
+		}	
+	}
+}
+
+void fscpos_wdog(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		results[0] = data->watchdog[0] ;
+		results[1] = data->watchdog[1] & 0x02;
+		results[2] = data->watchdog[2] & 0xb0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->watchdog[0] = results[0] & 0xff;
+			printk("fscpos: writing value 0x%02x to wdog_preset\n",
+				data->watchdog[0]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_PRESET,
+				data->watchdog[0]);
+		} 
+		if (*nrels_mag >= 2) {
+			data->watchdog[1] = results[1] & 0x02;
+			printk("fscpos: writing value 0x%02x to wdog_state\n",
+				data->watchdog[1]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_STATE,
+				data->watchdog[1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->watchdog[2] = results[2] & 0xb0;
+			printk("fscpos: writing value 0x%02x to wdog_control\n",
+				data->watchdog[2]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_CONTROL,
+				data->watchdog[2]);
+		}
+	}
+}
+
+static int __init sm_fscpos_init(void)
+{
+	printk("fscpos.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&fscpos_driver);
+}
+
+static void __exit sm_fscpos_exit(void)
+{
+	i2c_del_driver(&fscpos_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Hermann Jung <hej@odn.de> based on work from Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_fscpos_init);
+module_exit(sm_fscpos_exit);
Index: /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83627hf.c
===================================================================
--- /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83627hf.c	(revision 3228)
+++ /lm-sensors/branches/scanner-opt-branch/kernel/chips/w83627hf.c	(revision 3228)
@@ -0,0 +1,1502 @@
+/*
+    w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998 - 2003  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
+    w83627hf	9	3	2	3	0x20	0x5ca3	no	yes(LPC)
+    w83627thf	7	3	3	3	0x90	0x5ca3	no	yes(LPC)
+    w83637hf	7	3	3	3	0x80	0x5ca3	no	yes(LPC)
+    w83687thf	7	3	3	3	0x90	0x5ca3	no	yes(LPC)
+    w83697hf	8	2	2	2	0x60	0x5ca3	no	yes(LPC)
+
+    For other winbond chips, and for i2c support in the above chips,
+    use w83781d.c.
+
+    Note: automatic ("cruise") fan control for 697, 637 & 627thf not
+    supported yet.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include "version.h"
+#include "sensors_vid.h"
+#include "lm75.h"
+
+static int force_addr;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+static int force_i2c = 0x1f;
+MODULE_PARM(force_i2c, "i");
+MODULE_PARM_DESC(force_i2c,
+		 "Initialize the i2c address of the sensors");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_5(w83627hf, w83627thf, w83697hf, w83637hf, w83687thf);
+
+static int init = 1;
+MODULE_PARM(init, "i");
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+/* modified from kernel/include/traps.c */
+static int REG;		/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+static int VAL;		/* The value to read/write */
+
+/* logical device numbers for superio_select (below) */
+#define W83627HF_LD_FDC		0x00
+#define W83627HF_LD_PRT		0x01
+#define W83627HF_LD_UART1	0x02
+#define W83627HF_LD_UART2	0x03
+#define W83627HF_LD_KBC		0x05
+#define W83627HF_LD_CIR		0x06 /* w83627hf only */
+#define W83627HF_LD_GAME	0x07
+#define W83627HF_LD_MIDI	0x07
+#define W83627HF_LD_GPIO1	0x07
+#define W83627HF_LD_GPIO5	0x07 /* w83627thf only */
+#define W83627HF_LD_GPIO2	0x08
+#define W83627HF_LD_GPIO3	0x09
+#define W83627HF_LD_GPIO4	0x09 /* w83627thf only */
+#define W83627HF_LD_ACPI	0x0a
+#define W83627HF_LD_HWM		0x0b
+
+#define	DEVID	0x20	/* Register: Device ID */
+
+#define W83627THF_GPIO5_EN	0x30 /* w83627thf only */
+#define W83627THF_GPIO5_IOSR	0xf3 /* w83627thf only */
+#define W83627THF_GPIO5_DR	0xf4 /* w83627thf only */
+
+#define W83687THF_VID_EN	0x29 /* w83687thf only */
+#define W83687THF_VID_CFG	0xF0 /* w83687thf only */
+#define W83687THF_VID_DATA	0xF1 /* w83687thf only */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(int ld)
+{
+	outb(DEV, REG);
+	outb(ld, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x87, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+#define W627_DEVID 0x52
+#define W627THF_DEVID 0x82
+#define W697_DEVID 0x60
+#define W637_DEVID 0x70
+#define W687THF_DEVID 0x85
+#define WINB_ACT_REG 0x30
+#define WINB_BASE_REG 0x60
+/* Constants specified below */
+
+/* Length of ISA address segment */
+#define WINB_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_TEMP2 0x0150
+#define W83781D_REG_TEMP3 0x0250
+#define W83781D_REG_TEMP2_HYST 0x153
+#define W83781D_REG_TEMP3_HYST 0x253
+#define W83781D_REG_TEMP2_CONFIG 0x152
+#define W83781D_REG_TEMP3_CONFIG 0x252
+#define W83781D_REG_TEMP2_OVER 0x155
+#define W83781D_REG_TEMP3_OVER 0x255
+
+#define W83781D_REG_TEMP 0x27
+#define W83781D_REG_TEMP_OVER 0x39
+#define W83781D_REG_TEMP_HYST 0x3A
+#define W83781D_REG_BANK 0x4E
+
+#define W83781D_REG_CONFIG 0x40
+#define W83781D_REG_ALARM1 0x459
+#define W83781D_REG_ALARM2 0x45A
+#define W83781D_REG_ALARM3 0x45B
+
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453
+
+#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
+
+#define W83781D_REG_VBAT 0x5D
+
+#define W83627HF_REG_PWM1 0x5A
+#define W83627HF_REG_PWM2 0x5B
+
+#define W83627THF_REG_PWM1		0x01	/* 697HF/637HF/687THF too */
+#define W83627THF_REG_PWM2		0x03	/* 697HF/637HF/687THF too */
+#define W83627THF_REG_PWM3		0x11	/* 637HF/687THF too */
+
+#define W83627THF_REG_VRM_OVT_CFG 	0x18	/* 637HF/687THF too */
+
+static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 };
+static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2,
+                             W83627THF_REG_PWM3 };
+#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \
+                                     regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1])
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* Sensor selection */
+#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
+
+/* Conversions. 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 + 5) / 10)
+
+#define IN_TO_REG_VRM9(val) \
+	(SENSORS_LIMIT((((val) * 1000 - 70000 + 244) / 488), 0, 255))
+#define IN_FROM_REG_VRM9(reg)	(((reg) * 488 + 70000 + 500) / 1000)
+
+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 TEMP_MIN (-1280)
+#define TEMP_MAX ( 1270)
+
+/* TEMP: 1/10 degrees C (-128C to +127C)
+   REG: 1C/bit, two's complement */
+static u8 TEMP_TO_REG(int temp)
+{
+	int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX);
+	ntemp += (ntemp<0 ? -5 : 5);
+	return (u8)(ntemp / 10);
+}
+
+static int TEMP_FROM_REG(u8 reg)
+{
+	return (s8)reg * 10;
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define BEEPS_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)
+{
+	int i;
+	val = SENSORS_LIMIT(val, 1, 128) >> 1;
+	for (i = 0; i < 7; i++) {
+		if (val == 0)
+			break;
+		val >>= 1;
+	}
+	return ((u8) i);
+}
+
+/* For each registered chip, we need to keep some data in memory. That
+   data is pointed to by w83627hf_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new client is allocated. */
+struct w83627hf_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	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 in[9];		/* Register value */
+	u8 in_max[9];		/* Register value */
+	u8 in_min[9];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp;
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* Register value */
+	u16 temp_add[2];	/* Register value */
+	u16 temp_add_over[2];	/* Register value */
+	u16 temp_add_hyst[2];	/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u32 alarms;		/* Register encoding, combined */
+	u32 beeps;		/* Register encoding, combined */
+	u8 beep_enable;		/* Boolean */
+	u8 pwm[3];		/* Register value */
+	u16 sens[3];		/* 782D/783S only.
+				   1 = pentium diode; 2 = 3904 diode;
+				   3000-5000 = thermistor beta.
+				   Default = 3435.
+				   Other Betas unimplemented */
+	u8 vrm;
+	u8 vrm_ovt;		/* Register value, 627THF/637HF/687THF only */
+};
+
+
+static int w83627hf_attach_adapter(struct i2c_adapter *adapter);
+static int w83627hf_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int w83627hf_detach_client(struct i2c_client *client);
+
+static int w83627hf_read_value(struct i2c_client *client, u16 register);
+static int w83627hf_write_value(struct i2c_client *client, u16 register,
+			       u16 value);
+static void w83627hf_update_client(struct i2c_client *client);
+static void w83627hf_init_client(struct i2c_client *client);
+
+
+static void w83627hf_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_temp_add(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_beep(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_sens(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver w83627hf_driver = {
+	.name		= "W83627HF sensor driver",
+	.id		= I2C_DRIVERID_W83627HF,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83627hf_attach_adapter,
+	.detach_client	= w83627hf_detach_client,
+};
+
+/* The /proc/sys entries */
+/* WARNING these are copied from w83781d.c and have not been renamed.
+   Note that the 627hf is supported by both drivers.
+   Do not make incompatible changes here or we will have errors
+   in the generated file ../include/sensors.h !!!
+*/
+/* -- SENSORS SYSCTL START -- */
+
+#define W83781D_SYSCTL_IN0 1000	/* Volts * 100 */
+#define W83781D_SYSCTL_IN1 1001
+#define W83781D_SYSCTL_IN2 1002
+#define W83781D_SYSCTL_IN3 1003
+#define W83781D_SYSCTL_IN4 1004
+#define W83781D_SYSCTL_IN5 1005
+#define W83781D_SYSCTL_IN6 1006
+#define W83781D_SYSCTL_IN7 1007
+#define W83781D_SYSCTL_IN8 1008
+#define W83781D_SYSCTL_FAN1 1101	/* Rotations/min */
+#define W83781D_SYSCTL_FAN2 1102
+#define W83781D_SYSCTL_FAN3 1103
+#define W83781D_SYSCTL_TEMP1 1200	/* Degrees Celsius * 10 */
+#define W83781D_SYSCTL_TEMP2 1201	/* Degrees Celsius * 10 */
+#define W83781D_SYSCTL_TEMP3 1202	/* Degrees Celsius * 10 */
+#define W83781D_SYSCTL_VID 1300		/* Volts * 1000 */
+#define W83781D_SYSCTL_VRM 1301
+#define W83781D_SYSCTL_PWM1 1401
+#define W83781D_SYSCTL_PWM2 1402
+#define W83781D_SYSCTL_PWM3 1403
+#define W83781D_SYSCTL_SENS1 1501	/* 1, 2, or Beta (3000-5000) */
+#define W83781D_SYSCTL_SENS2 1502
+#define W83781D_SYSCTL_SENS3 1503
+#define W83781D_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define W83781D_SYSCTL_ALARMS 2001	/* bitvector */
+#define W83781D_SYSCTL_BEEP 2002	/* bitvector */
+
+#define W83781D_ALARM_IN0 0x0001
+#define W83781D_ALARM_IN1 0x0002
+#define W83781D_ALARM_IN2 0x0004
+#define W83781D_ALARM_IN3 0x0008
+#define W83781D_ALARM_IN4 0x0100
+#define W83781D_ALARM_IN5 0x0200
+#define W83781D_ALARM_IN6 0x0400
+#define W83782D_ALARM_IN7 0x10000
+#define W83782D_ALARM_IN8 0x20000
+#define W83781D_ALARM_FAN1 0x0040
+#define W83781D_ALARM_FAN2 0x0080
+#define W83781D_ALARM_FAN3 0x0800
+#define W83781D_ALARM_TEMP1 0x0010
+#define W83781D_ALARM_TEMP23 0x0020	/* 781D only */
+#define W83781D_ALARM_TEMP2 0x0020	/* 782D/783S */
+#define W83781D_ALARM_TEMP3 0x2000	/* 782D only */
+#define W83781D_ALARM_CHAS 0x1000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected chip. 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. */
+
+/* without pwm3-4 */
+static ctl_table w83627hf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+/* similar to w83782d but no fan3, no vid */
+static ctl_table w83697hf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	/* no in1 to maintain compatibility with 781d and 782d. */
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+/* no in5 and in6 */
+/* We use this one for W83637HF and W83687THF too */
+static ctl_table w83627thf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+
+/* This function is called when:
+     * w83627hf_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and w83627hf_driver is still present) */
+static int w83627hf_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, w83627hf_detect);
+}
+
+static int __init w83627hf_find(int sioaddr, int *address)
+{
+	u16 val;
+
+	REG = sioaddr;
+	VAL = sioaddr + 1;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if (val != W627_DEVID && val != W627THF_DEVID && val != W697_DEVID
+	 && val != W637_DEVID && val != W687THF_DEVID) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select(W83627HF_LD_HWM);
+	val = (superio_inb(WINB_BASE_REG) << 8) |
+	       superio_inb(WINB_BASE_REG + 1);
+	*address = val & ~(WINB_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("w83627hf.o: base address not set - use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+int w83627hf_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i, val;
+	struct i2c_client *new_client;
+	struct w83627hf_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_is_isa_adapter(adapter))
+		return 0;
+
+	if(force_addr)
+		address = force_addr & ~(WINB_EXTENT - 1);
+	if (check_region(address, WINB_EXTENT)) {
+		printk("w83627hf.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("w83627hf.o: forcing ISA address 0x%04X\n", address);
+		superio_enter();
+		superio_select(W83627HF_LD_HWM);
+		superio_outb(WINB_BASE_REG, address >> 8);
+		superio_outb(WINB_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(val == W627_DEVID)
+		kind = w83627hf;
+	else if(val == W697_DEVID)
+		kind = w83697hf;
+	else if(val == W627THF_DEVID)
+		kind = w83627thf;
+	else if(val == W637_DEVID)
+		kind = w83637hf;
+	else if (val == W687THF_DEVID)
+		kind = w83687thf;
+
+	superio_select(W83627HF_LD_HWM);
+	if((val = 0x01 & superio_inb(WINB_ACT_REG)) == 0)
+		superio_outb(WINB_ACT_REG, 1);
+	superio_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.
+	   But it allows us to access w83627hf_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83627hf_driver;
+	new_client->flags = 0;
+
+
+	if (kind == w83627hf) {
+		type_name = "w83627hf";
+		client_name = "W83627HF chip";
+	} else if (kind == w83627thf) {
+		type_name = "w83627thf";
+		client_name = "W83627THF chip";
+	} else if (kind == w83697hf) {
+		type_name = "w83697hf";
+		client_name = "W83697HF chip";
+	} else if (kind == w83637hf) {
+		type_name = "w83637hf";
+		client_name = "W83637HF chip";
+	} else if (kind == w83687thf) {
+		type_name = "w83687thf";
+		client_name = "W83687THF chip";
+	} else {
+		goto ERROR1;
+	}
+
+	request_region(address, WINB_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	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;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+				type_name,
+				(kind == w83697hf) ?
+				   w83697hf_dir_table_template :
+				(kind == w83627hf) ?
+				   w83627hf_dir_table_template :
+				   /* w83627thf table also used for 637HF
+				      and 687THF */
+				   w83627thf_dir_table_template,
+				THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR7;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the chip */
+	w83627hf_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+ERROR7:
+	i2c_detach_client(new_client);
+ERROR3:
+	release_region(address, WINB_EXTENT);
+ERROR1:
+	kfree(data);
+ERROR0:
+	return err;
+}
+
+static int w83627hf_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct w83627hf_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    (KERN_ERR "w83627hf.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, WINB_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/*
+   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 w83627hf_read_value(struct i2c_client *client, u16 reg)
+{
+	int res, word_sized;
+
+	down(&(((struct w83627hf_data *) (client->data))->lock));
+	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);
+	}
+	up(&(((struct w83627hf_data *) (client->data))->lock));
+	return res;
+}
+
+static int w83627thf_read_gpio5(struct i2c_client *client)
+{
+	int res = 0xff, sel;
+
+	superio_enter();
+	superio_select(W83627HF_LD_GPIO5);
+
+	/* Make sure these GPIO pins are enabled */
+	if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83627hf: GPIO5 disabled, no VID "
+		       "function\n");
+#endif
+		goto exit;
+	}
+
+	/* Make sure the pins are configured for input
+	   There must be at least five (VRM 9), and possibly 6 (VRM 10) */
+	sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f;
+	if ((sel & 0x1f) != 0x1f) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83627hf: GPIO5 not configured for "
+		       "VID function\n");
+#endif
+		goto exit;
+	}
+
+	printk(KERN_INFO "w83627hf: Reading VID from GPIO5\n");
+	res = superio_inb(W83627THF_GPIO5_DR) & sel;
+
+exit:
+	superio_exit();
+	return res;
+}
+
+static int w83687thf_read_vid(struct i2c_client *client)
+{
+	int res = 0xff;
+
+	superio_enter();
+	superio_select(W83627HF_LD_HWM);
+
+	/* Make sure these GPIO pins are enabled */
+	if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83627hf: VID disabled, no VID "
+		       "function\n");
+#endif
+		goto exit;
+	}
+
+	/* Make sure the pins are configured for input */
+	if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83627hf: VID configured as output, "
+		       "no VID function\n");
+#endif
+		goto exit;
+	}
+
+	res = superio_inb(W83687THF_VID_DATA) & 0x3f;
+
+exit:
+	superio_exit();
+	return res;
+}
+
+static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+	int word_sized;
+
+	down(&(((struct