Index: /lm-sensors/tags/V2-5-5/BACKGROUND
===================================================================
--- /lm-sensors/tags/V2-5-5/BACKGROUND	(revision 568)
+++ /lm-sensors/tags/V2-5-5/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/tags/V2-5-5/kernel/include/sensors.h
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/include/sensors.h	(revision 963)
+++ /lm-sensors/tags/V2-5-5/kernel/include/sensors.h	(revision 963)
@@ -0,0 +1,836 @@
+/*
+    sensors.h - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef SENSORS_SENSORS_H
+#define SENSORS_SENSORS_H
+
+#ifdef __KERNEL__
+
+/* Next two must be included before sysctl.h can be included, in 2.0 kernels */
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+
+/* The type of callback functions used in sensors_{proc,sysctl}_real */
+typedef void (*sensors_real_callback) (struct i2c_client * client,
+				       int operation, int ctl_name,
+				       int *nrels_mag, long *results);
+
+/* Values for the operation field in the above function type */
+#define SENSORS_PROC_REAL_INFO 1
+#define SENSORS_PROC_REAL_READ 2
+#define SENSORS_PROC_REAL_WRITE 3
+
+/* These funcion reads or writes a 'real' value (encoded by the combination
+   of an integer and a magnitude, the last is the power of ten the value
+   should be divided with) to a /proc/sys directory. To use these functions,
+   you must (before registering the ctl_table) set the extra2 field to the
+   client, and the extra1 field to a function of the form:
+      void func(struct i2c_client *client, int operation, int ctl_name,
+                int *nrels_mag, long *results)
+   This last function can be called for three values of operation. If
+   operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned
+   in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
+   be read into results. nrels_mag should return the number of elements
+   read; the maximum number is put in it on entry. Finally, if operation
+   equals SENSORS_PROC_REAL_WRITE, the values in results should be
+   written to the chip. nrels_mag contains on entry the number of elements
+   found.
+   In all cases, client points to the client we wish to interact with,
+   and ctl_name is the SYSCTL id of the file we are accessing. */
+extern int sensors_sysctl_real(ctl_table * table, int *name, int nlen,
+			       void *oldval, size_t * oldlenp,
+			       void *newval, size_t newlen,
+			       void **context);
+extern int sensors_proc_real(ctl_table * ctl, int write, struct file *filp,
+			     void *buffer, size_t * lenp);
+
+
+
+/* These rather complex functions must be called when you want to add or
+   delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It
+   also creates a new directory within /proc/sys/dev/sensors/.
+   ctl_template should be a template of the newly created directory. It is
+   copied in memory. The extra2 field of each file is set to point to client.
+   If any driver wants subdirectories within the newly created directory,
+   these functions must be updated! */
+extern int sensors_register_entry(struct i2c_client *client,
+				  const char *prefix,
+				  ctl_table * ctl_template,
+				  struct module *controlling_mod);
+
+extern void sensors_deregister_entry(int id);
+
+
+/* A structure containing detect information.
+   Force variables overrule all other variables; they force a detection on
+   that place. If a specific chip is given, the module blindly assumes this
+   chip type is present; if a general force (kind == 0) is given, the module
+   will still try to figure out what type of chip is present. This is useful
+   if for some reasons the detect for SMBus or ISA address space filled
+   fails.
+   probe: insmod parameter. Initialize this list with SENSORS_I2C_END values.
+     A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for
+     the ISA bus, -1 for any I2C bus), the second is the address. 
+   kind: The kind of chip. 0 equals any chip.
+*/
+struct sensors_force_data {
+	unsigned short *force;
+	unsigned short kind;
+};
+
+/* A structure containing the detect information.
+   normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END.
+     A list of I2C addresses which should normally be examined.
+   normal_i2c_range: filled in by the module writer. Terminated by 
+     SENSORS_I2C_END
+     A list of pairs of I2C addresses, each pair being an inclusive range of
+     addresses which should normally be examined.
+   normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END.
+     A list of ISA addresses which should normally be examined.
+   normal_isa_range: filled in by the module writer. Terminated by 
+     SENSORS_ISA_END
+     A list of triples. The first two elements are ISA addresses, being an
+     range of addresses which should normally be examined. The third is the
+     modulo parameter: only addresses which are 0 module this value relative
+     to the first address of the range are actually considered.
+   probe: insmod parameter. Initialize this list with SENSORS_I2C_END values.
+     A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for
+     the ISA bus, -1 for any I2C bus), the second is the address. These
+     addresses are also probed, as if they were in the 'normal' list.
+   probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END 
+     values.
+     A list of triples. The first value is a bus number (SENSORS_ISA_BUS for
+     the ISA bus, -1 for any I2C bus), the second and third are addresses. 
+     These form an inclusive range of addresses that are also probed, as
+     if they were in the 'normal' list.
+   ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values.
+     A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for
+     the ISA bus, -1 for any I2C bus), the second is the I2C address. These
+     addresses are never probed. This parameter overrules 'normal' and 
+     'probe', but not the 'force' lists.
+   ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END 
+      values.
+     A list of triples. The first value is a bus number (SENSORS_ISA_BUS for
+     the ISA bus, -1 for any I2C bus), the second and third are addresses. 
+     These form an inclusive range of I2C addresses that are never probed.
+     This parameter overrules 'normal' and 'probe', but not the 'force' lists.
+   force_data: insmod parameters. A list, ending with an element of which
+     the force field is NULL.
+*/
+struct sensors_address_data {
+	unsigned short *normal_i2c;
+	unsigned short *normal_i2c_range;
+	unsigned int *normal_isa;
+	unsigned int *normal_isa_range;
+	unsigned short *probe;
+	unsigned short *probe_range;
+	unsigned short *ignore;
+	unsigned short *ignore_range;
+	struct sensors_force_data *forces;
+};
+
+/* Internal numbers to terminate lists */
+#define SENSORS_I2C_END 0xfffe
+#define SENSORS_ISA_END 0xfffefffe
+
+/* The numbers to use to set an ISA or I2C bus address */
+#define SENSORS_ISA_BUS 9191
+#define SENSORS_ANY_I2C_BUS 0xffff
+
+/* The length of the option lists */
+#define SENSORS_MAX_OPTS 48
+
+/* Default fill of many variables */
+#define SENSORS_DEFAULTS {SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END, \
+                          SENSORS_I2C_END, SENSORS_I2C_END, SENSORS_I2C_END}
+
+/* This is ugly. We need to evaluate SENSORS_MAX_OPTS before it is 
+   stringified */
+#define SENSORS_MODPARM_AUX1(x) "1-" #x "h"
+#define SENSORS_MODPARM_AUX(x) SENSORS_MODPARM_AUX1(x)
+#define SENSORS_MODPARM SENSORS_MODPARM_AUX(SENSORS_MAX_OPTS)
+
+/* SENSORS_MODULE_PARM creates a module parameter, and puts it in the
+   module header */
+#define SENSORS_MODULE_PARM(var,desc) \
+  static unsigned short var[SENSORS_MAX_OPTS] = SENSORS_DEFAULTS; \
+  MODULE_PARM(var,SENSORS_MODPARM); \
+  MODULE_PARM_DESC(var,desc)
+
+/* SENSORS_MODULE_PARM creates a 'force_*' module parameter, and puts it in
+   the module header */
+#define SENSORS_MODULE_PARM_FORCE(name) \
+  SENSORS_MODULE_PARM(force_ ## name, \
+                      "List of adapter,address pairs which are unquestionably" \
+                      " assumed to contain a `" # name "' chip")
+
+
+/* This defines several insmod variables, and the addr_data structure */
+#define SENSORS_INSMOD \
+  SENSORS_MODULE_PARM(probe, \
+                      "List of adapter,address pairs to scan additionally"); \
+  SENSORS_MODULE_PARM(probe_range, \
+                      "List of adapter,start-addr,end-addr triples to scan " \
+                      "additionally"); \
+  SENSORS_MODULE_PARM(ignore, \
+                      "List of adapter,address pairs not to scan"); \
+  SENSORS_MODULE_PARM(ignore_range, \
+                      "List of adapter,start-addr,end-addr triples not to " \
+                      "scan"); \
+  static struct sensors_address_data addr_data = \
+                                       {normal_i2c, normal_i2c_range, \
+                                        normal_isa, normal_isa_range, \
+                                        probe, probe_range, \
+                                        ignore, ignore_range, \
+                                        forces}
+
+/* The following functions create an enum with the chip names as elements. 
+   The first element of the enum is any_chip. These are the only macros
+   a module will want to use. */
+
+#define SENSORS_INSMOD_0 \
+  enum chips { any_chip }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  static struct sensors_force_data forces[] = {{force,any_chip},{NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_1(chip1) \
+  enum chips { any_chip, chip1 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  static struct sensors_force_data forces[] = {{force,any_chip},\
+                                                 {force_ ## chip1,chip1}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_2(chip1,chip2) \
+  enum chips { any_chip, chip1, chip2 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_3(chip1,chip2,chip3) \
+  enum chips { any_chip, chip1, chip2, chip3 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_4(chip1,chip2,chip3,chip4) \
+  enum chips { any_chip, chip1, chip2, chip3, chip4 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  SENSORS_MODULE_PARM_FORCE(chip4); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {force_ ## chip4,chip4}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_5(chip1,chip2,chip3,chip4,chip5) \
+  enum chips { any_chip, chip1, chip2, chip3, chip4, chip5 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  SENSORS_MODULE_PARM_FORCE(chip4); \
+  SENSORS_MODULE_PARM_FORCE(chip5); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {force_ ## chip4,chip4}, \
+                                                 {force_ ## chip5,chip5}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_6(chip1,chip2,chip3,chip4,chip5,chip6) \
+  enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  SENSORS_MODULE_PARM_FORCE(chip4); \
+  SENSORS_MODULE_PARM_FORCE(chip5); \
+  SENSORS_MODULE_PARM_FORCE(chip6); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {force_ ## chip4,chip4}, \
+                                                 {force_ ## chip5,chip5}, \
+                                                 {force_ ## chip6,chip6}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+#define SENSORS_INSMOD_7(chip1,chip2,chip3,chip4,chip5,chip6,chip7) \
+  enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  SENSORS_MODULE_PARM_FORCE(chip4); \
+  SENSORS_MODULE_PARM_FORCE(chip5); \
+  SENSORS_MODULE_PARM_FORCE(chip6); \
+  SENSORS_MODULE_PARM_FORCE(chip7); \
+  static struct sensors_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {force_ ## chip4,chip4}, \
+                                                 {force_ ## chip5,chip5}, \
+                                                 {force_ ## chip6,chip6}, \
+                                                 {force_ ## chip7,chip7}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
+typedef int sensors_found_addr_proc(struct i2c_adapter *adapter,
+				    int addr, unsigned short flags,
+				    int kind);
+
+/* Detect function. It itterates over all possible addresses itself. For
+   SMBus addresses, it will only call found_proc if some client is connected
+   to the SMBus (unless a 'force' matched); for ISA detections, this is not
+   done. */
+extern int sensors_detect(struct i2c_adapter *adapter,
+			  struct sensors_address_data *address_data,
+			  sensors_found_addr_proc * found_proc);
+
+
+/* This macro is used to scale user-input to sensible values in almost all
+   chip drivers. */
+extern inline int SENSORS_LIMIT(long value, long low, long high)
+{
+	if (value < low)
+		return low;
+	else if (value > high)
+		return high;
+	else
+		return value;
+}
+
+#endif				/* def __KERNEL__ */
+
+
+/* The maximum length of the prefix */
+#define SENSORS_PREFIX_MAX 20
+
+/* IDs --   Use DRIVERIDs 1000-1999 for sensors. 
+   Other drivers define the id in linux/i2c.h     */
+#define I2C_DRIVERID_LM78 1002
+#define I2C_DRIVERID_LM75 1003
+#define I2C_DRIVERID_GL518 1004
+#define I2C_DRIVERID_EEPROM 1005
+#define I2C_DRIVERID_W83781D 1006
+#define I2C_DRIVERID_LM80 1007
+#define I2C_DRIVERID_ADM1021 1008
+#define I2C_DRIVERID_ADM9240 1009
+#define I2C_DRIVERID_LTC1710 1010
+#define I2C_DRIVERID_SIS5595 1011
+#define I2C_DRIVERID_ICSPLL 1012
+#define I2C_DRIVERID_BT869 1013
+#define I2C_DRIVERID_MAXILIFE 1014
+#define I2C_DRIVERID_MATORB 1015
+#define I2C_DRIVERID_GL520 1016
+#define I2C_DRIVERID_THMC50 1017
+#define I2C_DRIVERID_DDCMON 1018
+#define I2C_DRIVERID_VIA686A 1019
+#define I2C_DRIVERID_ADM1025 1020
+#define I2C_DRIVERID_LM87 1021
+#define I2C_DRIVERID_PCF8574 1022
+#define I2C_DRIVERID_MTP008 1023
+#define I2C_DRIVERID_DS1621 1024
+
+/* Sysctl IDs */
+#ifdef DEV_HWMON
+#define DEV_SENSORS DEV_HWMON
+#else				/* ndef DEV_HWMOM */
+#define DEV_SENSORS 2		/* The id of the lm_sensors directory within the
+				   dev table */
+#endif				/* def DEV_HWMON */
+
+#define SENSORS_CHIPS 1
+struct sensors_chips_data {
+	int sysctl_id;
+	char name[SENSORS_PREFIX_MAX + 13];
+};
+
+#define LM78_SYSCTL_IN0 1000	/* Volts * 100 */
+#define LM78_SYSCTL_IN1 1001
+#define LM78_SYSCTL_IN2 1002
+#define LM78_SYSCTL_IN3 1003
+#define LM78_SYSCTL_IN4 1004
+#define LM78_SYSCTL_IN5 1005
+#define LM78_SYSCTL_IN6 1006
+#define LM78_SYSCTL_FAN1 1101	/* Rotations/min */
+#define LM78_SYSCTL_FAN2 1102
+#define LM78_SYSCTL_FAN3 1103
+#define LM78_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define LM78_SYSCTL_VID 1300	/* Volts * 100 */
+#define LM78_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define LM78_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define LM78_ALARM_IN0 0x0001
+#define LM78_ALARM_IN1 0x0002
+#define LM78_ALARM_IN2 0x0004
+#define LM78_ALARM_IN3 0x0008
+#define LM78_ALARM_IN4 0x0100
+#define LM78_ALARM_IN5 0x0200
+#define LM78_ALARM_IN6 0x0400
+#define LM78_ALARM_FAN1 0x0040
+#define LM78_ALARM_FAN2 0x0080
+#define LM78_ALARM_FAN3 0x0800
+#define LM78_ALARM_TEMP 0x0010
+#define LM78_ALARM_BTI 0x0020
+#define LM78_ALARM_CHAS 0x1000
+#define LM78_ALARM_FIFO 0x2000
+#define LM78_ALARM_SMI_IN 0x4000
+
+#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 Celcius * 10 */
+#define W83781D_SYSCTL_TEMP2 1201	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_TEMP3 1202	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_VID 1300	/* Volts * 100 */
+#define W83781D_SYSCTL_PWM1 1401
+#define W83781D_SYSCTL_PWM2 1402
+#define W83781D_SYSCTL_PWM3 1403
+#define W83781D_SYSCTL_PWM4 1404
+#define W83781D_SYSCTL_SENS1 1501	/* 1, 2, or Beta (3000-5000) */
+#define W83781D_SYSCTL_SENS2 1502
+#define W83781D_SYSCTL_SENS3 1503
+#define W83781D_SYSCTL_RT1   1601	/* 32-entry table */
+#define W83781D_SYSCTL_RT2   1602	/* 32-entry table */
+#define W83781D_SYSCTL_RT3   1603	/* 32-entry table */
+#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
+
+#define LM75_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+
+#define ADM1021_SYSCTL_TEMP 1200
+#define ADM1021_SYSCTL_REMOTE_TEMP 1201
+#define ADM1021_SYSCTL_DIE_CODE 1202
+#define ADM1021_SYSCTL_ALARMS 1203
+
+#define ADM1021_ALARM_TEMP_HIGH 0x40
+#define ADM1021_ALARM_TEMP_LOW 0x20
+#define ADM1021_ALARM_RTEMP_HIGH 0x10
+#define ADM1021_ALARM_RTEMP_LOW 0x08
+#define ADM1021_ALARM_RTEMP_NA 0x04
+
+#define GL518_SYSCTL_VDD  1000	/* Volts * 100 */
+#define GL518_SYSCTL_VIN1 1001
+#define GL518_SYSCTL_VIN2 1002
+#define GL518_SYSCTL_VIN3 1003
+#define GL518_SYSCTL_FAN1 1101	/* RPM */
+#define GL518_SYSCTL_FAN2 1102
+#define GL518_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define GL518_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define GL518_SYSCTL_ALARMS 2001	/* bitvector */
+#define GL518_SYSCTL_BEEP 2002	/* bitvector */
+#define GL518_SYSCTL_FAN1OFF 2003
+#define GL518_SYSCTL_ITERATE 2004
+
+#define GL518_ALARM_VDD 0x01
+#define GL518_ALARM_VIN1 0x02
+#define GL518_ALARM_VIN2 0x04
+#define GL518_ALARM_VIN3 0x08
+#define GL518_ALARM_TEMP 0x10
+#define GL518_ALARM_FAN1 0x20
+#define GL518_ALARM_FAN2 0x40
+
+#define GL520_SYSCTL_VDD  1000	/* Volts * 100 */
+#define GL520_SYSCTL_VIN1 1001
+#define GL520_SYSCTL_VIN2 1002
+#define GL520_SYSCTL_VIN3 1003
+#define GL520_SYSCTL_VIN4 1004
+#define GL520_SYSCTL_FAN1 1101	/* RPM */
+#define GL520_SYSCTL_FAN2 1102
+#define GL520_SYSCTL_TEMP1 1200	/* Degrees Celcius * 10 */
+#define GL520_SYSCTL_TEMP2 1201	/* Degrees Celcius * 10 */
+#define GL520_SYSCTL_VID 1300
+#define GL520_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define GL520_SYSCTL_ALARMS 2001	/* bitvector */
+#define GL520_SYSCTL_BEEP 2002	/* bitvector */
+#define GL520_SYSCTL_FAN1OFF 2003
+#define GL520_SYSCTL_CONFIG 2004
+
+#define GL520_ALARM_VDD 0x01
+#define GL520_ALARM_VIN1 0x02
+#define GL520_ALARM_VIN2 0x04
+#define GL520_ALARM_VIN3 0x08
+#define GL520_ALARM_TEMP1 0x10
+#define GL520_ALARM_FAN1 0x20
+#define GL520_ALARM_FAN2 0x40
+#define GL520_ALARM_TEMP2 0x80
+#define GL520_ALARM_VIN4 0x80
+
+#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 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 Celcius * 100 */
+#define LM80_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define LM80_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define ADM9240_SYSCTL_IN0 1000	/* Volts * 100 */
+#define ADM9240_SYSCTL_IN1 1001
+#define ADM9240_SYSCTL_IN2 1002
+#define ADM9240_SYSCTL_IN3 1003
+#define ADM9240_SYSCTL_IN4 1004
+#define ADM9240_SYSCTL_IN5 1005
+#define ADM9240_SYSCTL_FAN1 1101	/* Rotations/min */
+#define ADM9240_SYSCTL_FAN2 1102
+#define ADM9240_SYSCTL_TEMP 1250	/* Degrees Celcius * 100 */
+#define ADM9240_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define ADM9240_SYSCTL_ALARMS 2001	/* bitvector */
+#define ADM9240_SYSCTL_ANALOG_OUT 2002
+#define ADM9240_SYSCTL_VID 2003
+
+#define ADM9240_ALARM_IN0 0x0001
+#define ADM9240_ALARM_IN1 0x0002
+#define ADM9240_ALARM_IN2 0x0004
+#define ADM9240_ALARM_IN3 0x0008
+#define ADM9240_ALARM_IN4 0x0100
+#define ADM9240_ALARM_IN5 0x0200
+#define ADM9240_ALARM_FAN1 0x0040
+#define ADM9240_ALARM_FAN2 0x0080
+#define ADM9240_ALARM_TEMP 0x0010
+#define ADM9240_ALARM_CHAS 0x1000
+
+#define ADM1025_SYSCTL_IN0 1000 /* Volts * 100 */
+#define ADM1025_SYSCTL_IN1 1001
+#define ADM1025_SYSCTL_IN2 1002
+#define ADM1025_SYSCTL_IN3 1003
+#define ADM1025_SYSCTL_IN4 1004
+#define ADM1025_SYSCTL_IN5 1005
+#define ADM1025_SYSCTL_RTEMP 1251
+#define ADM1025_SYSCTL_TEMP 1250        /* Degrees Celcius * 100 */
+#define ADM1025_SYSCTL_ALARMS 2001      /* bitvector */
+#define ADM1025_SYSCTL_ANALOG_OUT 2002
+#define ADM1025_SYSCTL_VID 2003
+
+#define ADM1025_ALARM_IN0 0x0001
+#define ADM1025_ALARM_IN1 0x0002
+#define ADM1025_ALARM_IN2 0x0004
+#define ADM1025_ALARM_IN3 0x0008
+#define ADM1025_ALARM_IN4 0x0100
+#define ADM1025_ALARM_IN5 0x0200
+#define ADM1025_ALARM_RTEMP 0x0020
+#define ADM1025_ALARM_TEMP 0x0010
+
+#define LTC1710_SYSCTL_SWITCH_1 1000
+#define LTC1710_SYSCTL_SWITCH_2 1001
+
+#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
+
+#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 Celcius */
+#define MAXI_SYSCTL_TEMP2  1202	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP3  1203	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP4  1204	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP5  1205	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP6  1206	/* Degrees Celcius */
+#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 */
+
+#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_FAN1 1101	/* Rotations/min */
+#define SIS5595_SYSCTL_FAN2 1102
+#define SIS5595_SYSCTL_TEMP 1200	/* Degrees Celcius * 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_TEMP 0x10
+#define SIS5595_ALARM_BTI 0x20
+#define SIS5595_ALARM_FAN1 0x40
+#define SIS5595_ALARM_FAN2 0x80
+
+#define VIA686A_SYSCTL_IN0 1000
+#define VIA686A_SYSCTL_IN1 1001
+#define VIA686A_SYSCTL_IN2 1002
+#define VIA686A_SYSCTL_IN3 1003
+#define VIA686A_SYSCTL_IN4 1004
+#define VIA686A_SYSCTL_FAN1 1101
+#define VIA686A_SYSCTL_FAN2 1102
+#define VIA686A_SYSCTL_TEMP 1200
+#define VIA686A_SYSCTL_TEMP2 1201
+#define VIA686A_SYSCTL_TEMP3 1202
+#define VIA686A_SYSCTL_FAN_DIV 2000
+#define VIA686A_SYSCTL_ALARMS 2001
+
+#define VIA686A_ALARM_IN0 0x01
+#define VIA686A_ALARM_IN1 0x02
+#define VIA686A_ALARM_IN2 0x04
+#define VIA686A_ALARM_IN3 0x08
+#define VIA686A_ALARM_TEMP 0x10
+#define VIA686A_ALARM_FAN1 0x40
+#define VIA686A_ALARM_FAN2 0x80
+#define VIA686A_ALARM_IN4 0x100
+#define VIA686A_ALARM_TEMP2 0x800
+#define VIA686A_ALARM_CHAS 0x1000
+#define VIA686A_ALARM_TEMP3 0x8000
+
+#define ICSPLL_SYSCTL1 1000
+
+#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 MATORB_SYSCTL_DISP 1000
+
+#define THMC50_SYSCTL_TEMP 1200	/* Degrees Celcius */
+#define THMC50_SYSCTL_REMOTE_TEMP 1201	/* Degrees Celcius */
+#define THMC50_SYSCTL_INTER 1202
+#define THMC50_SYSCTL_INTER_MASK 1203
+#define THMC50_SYSCTL_DIE_CODE 1204
+#define THMC50_SYSCTL_ANALOG_OUT 1205
+
+#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 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 Celcius * 100 */
+#define LM87_SYSCTL_TEMP2   1251 /* Degrees Celcius * 100 */
+#define LM87_SYSCTL_TEMP3   1252 /* Degrees Celcius * 100 */
+#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_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
+
+#define PCF8574_SYSCTL_STAT 1000
+
+#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 Celcius * 10 */
+#define MTP008_SYSCTL_TEMP2	1201	/* Degrees Celcius * 10 */
+#define MTP008_SYSCTL_TEMP3	1202	/* Degrees Celcius * 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
+
+#define DS1621_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define DS1621_SYSCTL_ALARMS 2001	/* bitvector */
+#define DS1621_ALARM_TEMP_HIGH 0x40
+#define DS1621_ALARM_TEMP_LOW 0x20
+#define DS1621_SYSCTL_ENABLE 2002
+
+#endif				/* def SENSORS_SENSORS_H */
+
Index: /lm-sensors/tags/V2-5-5/kernel/include/Module.mk
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/include/Module.mk	(revision 681)
+++ /lm-sensors/tags/V2-5-5/kernel/include/Module.mk	(revision 681)
@@ -0,0 +1,43 @@
+#  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)
+
+KERNELINCLUDEFILES := 
+ifneq ($(shell if grep -q '^CONFIG_SENSORS=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELINCLUDEFILES += $(MODULE_DIR)/sensors.h
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_ISA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELINCLUDEFILES += $(MODULE_DIR)/i2c-isa.h
+endif
+
+install-all-kernel-include:
+	if [ -n "$(KERNELINCLUDEFILES)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(SYSINCLUDEDIR) ; \
+	  $(INSTALL) -o root -g root -m 644 $(KERNELINCLUDEFILES) $(DESTDIR)$(SYSINCLUDEDIR) ; \
+	fi
+
+install :: install-all-kernel-include
+
+clean-all-kernel-include:
+	$(RM) $(KERNELINCLUDEDIR)/*.h.install
+
+clean :: clean-all-kernel-include
Index: /lm-sensors/tags/V2-5-5/kernel/include/i2c-isa.h
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/include/i2c-isa.h	(revision 707)
+++ /lm-sensors/tags/V2-5-5/kernel/include/i2c-isa.h	(revision 707)
@@ -0,0 +1,38 @@
+/*
+    i2c-isa.h - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#ifndef SENSORS_SENSORS_ISA_H
+#define SENSORS_SENSORS_ISA_H
+
+#ifdef __KERNEL__
+
+#include <linux/i2c.h>
+
+/* Detect whether we are on the isa bus. If this returns true, all i2c
+   access will fail! */
+#define i2c_is_isa_client(clientptr) \
+        ((clientptr)->adapter->algo->id == I2C_ALGO_ISA)
+#define i2c_is_isa_adapter(adapptr) \
+        ((adapptr)->algo->id == I2C_ALGO_ISA)
+
+#endif				/* def __KERNEL__ */
+
+#endif				/* ndef SENSORS_ISA_H */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali1535.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali1535.c	(revision 949)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali1535.c	(revision 949)
@@ -0,0 +1,687 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.h>
+
+#ifndef DECLARE_MUTEX
+#define DECLARE_MUTEX(name)  struct semaphore name = MUTEX
+#endif /* def DECLARE_MUTEX */
+
+/* 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        */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_ali1535_init(void);
+static int __init ali1535_cleanup(void);
+static int ali1535_setup(void);
+static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
+			  unsigned short flags, char read_write,
+			  u8 command, int size,
+			  union i2c_smbus_data *data);
+static void ali1535_do_pause(unsigned int amount);
+static int ali1535_transaction(void);
+static void ali1535_inc(struct i2c_adapter *adapter);
+static void ali1535_dec(struct i2c_adapter *adapter);
+static u32 ali1535_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-i2c SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL, 
+	/* smbus_access */ ali1535_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ ali1535_func,
+};
+
+static struct i2c_adapter ali1535_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535,
+	&smbus_algorithm,
+	NULL,
+	ali1535_inc,
+	ali1535_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata ali1535_initialized;
+static unsigned short ali1535_smba = 0;
+DECLARE_MUTEX(i2c_ali1535_sem);
+
+
+/* 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(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	struct pci_dev *ALI1535_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-ali1535.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the ALI1535, M7101 device */
+	ALI1535_dev = NULL;
+	ALI1535_dev = pci_find_device(PCI_VENDOR_ID_AL,
+				      PCI_DEVICE_ID_AL_M7101, 
+				      ALI1535_dev); 
+
+	if (ALI1535_dev == NULL) {
+		printk("i2c-ali1535.o: Error: Can't detect ali1535!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* 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
+		    ("SMBUS: Error: 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
+		    ("SMBUS: Error: Host 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-smb");
+
+#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;
+}
+
+
+/* Internally used pause function */
+void ali1535_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int ali1535_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-ali1535.o: Transaction (pre): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
+	     "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP),
+	     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+	     inb_p(SMBHSTDAT1));
+#endif
+
+	/* get status */
+	temp = inb_p(SMBHSTSTS);
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* Check the busy bit first */
+	if (temp & ALI1535_STS_BUSY) {
+/*
+   If the host controller is still busy, it may have timed out in the previous transaction,
+   resulting in a "SMBus Timeout" printk.
+   I've tried the following to reset a stuck busy bit.
+	1. Reset the controller with an KILL 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 */
+/*
+#ifdef DEBUG
+    printk("i2c-ali1535.o: Resetting host controller to clear busy condition\n",temp);
+#endif
+    outb_p(ALI1535_KILL, SMBHSTTYP);
+    temp = inb_p(SMBHSTSTS);
+    if (temp & ALI1535_STS_BUSY) {
+*/
+
+/*
+   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.
+*/
+		printk
+		    ("i2c-ali1535.o: Resetting entire SMB Bus to clear busy condition (%02x)\n",
+		     temp);
+		outb_p(ALI1535_T_OUT, SMBHSTTYP);
+		temp = inb_p(SMBHSTSTS);
+	}
+/*
+  }
+*/
+
+	/* now check the error bits and the busy bit */
+	if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
+		/* do a clear-on-write */
+		outb_p(0xFF, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) &
+		    (ALI1535_STS_ERR | ALI1535_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. */
+			printk
+			    ("i2c-ali1535.o: 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 & ALI1535_STS_DONE) {
+			outb_p(temp, SMBHSTSTS);
+		}
+	}
+
+	/* start the transaction by writing anything to the start register */
+	outb_p(0xFF, SMBHSTPORT);
+
+	/* We will always wait for a fraction of a second! */
+	timeout = 0;
+	do {
+		ali1535_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	} while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE))
+		 && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		printk("i2c-ali1535.o: SMBus Timeout!\n");
+	}
+
+	if (temp & ALI1535_STS_FAIL) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-ali1535.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+/*
+  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 & ALI1535_STS_BUSERR) {
+		result = -1;
+#ifdef DEBUG
+		printk
+		    ("i2c-ali1535.o: Error: no response or bus collision ADD=%02x\n",
+		     inb_p(SMBHSTADD));
+#endif
+	}
+
+/* haven't ever seen this */
+	if (temp & ALI1535_STS_DEV) {
+		result = -1;
+		printk("i2c-ali1535.o: Error: device error\n");
+	}
+
+/* 
+   check to see if the "command complete" indication is set
+ */
+	if (!(temp & ALI1535_STS_DONE)) {
+		result = -1;
+		printk("i2c-ali1535.o: Error: command never completed\n");
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-ali1535.o: Transaction (post): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, "
+	     "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP),
+	     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+	     inb_p(SMBHSTDAT1));
+#endif
+
+/* 
+    take consequent actions for error conditions
+ */
+        if (!(temp & ALI1535_STS_DONE)) {
+	  /* issue "kill" to reset host controller */
+	  outb_p(ALI1535_KILL,SMBHSTTYP);
+	  outb_p(0xFF,SMBHSTSTS);
+	}	  
+	else if (temp & ALI1535_STS_ERR) {
+	  /* issue "timeout" to reset all devices on bus */
+	  outb_p(ALI1535_T_OUT,SMBHSTTYP);
+	  outb_p(0xFF,SMBHSTSTS);
+	}
+        
+	return result;
+}
+
+/* Return -1 on error. See smbus.h for more information */
+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;
+	int temp;
+	int timeout;
+	s32 result = 0;
+
+	down(&i2c_ali1535_sem);
+/* make sure SMBus is idle */
+	temp = inb_p(SMBHSTSTS);
+	for (timeout = 0;
+	     (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE);
+	     timeout++) {
+		ali1535_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	}
+	if (timeout >= MAX_TIMEOUT) {
+		printk("i2c-ali1535.o: Idle wait Timeout! STS=0x%02x\n",
+		       temp);
+	}
+
+/* clear status register (clear-on-write) */
+	outb_p(0xFF, SMBHSTSTS);
+
+	switch (size) {
+	case I2C_SMBUS_PROC_CALL:
+		printk
+		    ("i2c-ali1535.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		result = -1;
+		goto EXIT;
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_QUICK;
+                outb_p(size, SMBHSTTYP);	/* output command */
+                break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BYTE;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BYTE_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_WORD_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BLOCK_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		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);
+			outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		break;
+	}
+
+	if (ali1535_transaction())	/* Error in transaction */
+	  {
+		result = -1;
+		goto EXIT;
+          }
+
+	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);
+		break;
+	case ALI1535_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI1535_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case ALI1535_BLOCK_DATA:
+		len = inb_p(SMBHSTDAT0);
+		if (len > 32)
+			len = 32;
+		data->block[0] = len;
+		outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+#ifdef DEBUG
+			printk
+			    ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n",
+			     len, i, data->block[i]);
+#endif	/* DEBUG */
+		}
+		break;
+	}
+EXIT:
+	up(&i2c_ali1535_sem);
+	return result;
+}
+
+void ali1535_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void ali1535_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_ali1535_init(void)
+{
+	int res;
+	printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (ali1535_initialized) {
+		printk
+		    ("i2c-ali1535.o: Oops, ali1535_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	ali1535_initialized = 0;
+	if ((res = ali1535_setup())) {
+		printk
+		    ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n");
+		ali1535_cleanup();
+		return res;
+	}
+	ali1535_initialized++;
+	sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x",
+		ali1535_smba);
+	if ((res = i2c_add_adapter(&ali1535_adapter))) {
+		printk
+		    ("i2c-ali1535.o: Adapter registration failed, module not inserted.\n");
+		ali1535_cleanup();
+		return res;
+	}
+	ali1535_initialized++;
+	printk
+	    ("i2c-ali1535.o: ALI1535 SMBus Controller detected and initialized\n");
+	return 0;
+}
+
+int __init ali1535_cleanup(void)
+{
+	int res;
+	if (ali1535_initialized >= 2) {
+		if ((res = i2c_del_adapter(&ali1535_adapter))) {
+			printk
+			    ("i2c-ali1535.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			ali1535_initialized--;
+	}
+	if (ali1535_initialized >= 1) {
+		release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+		ali1535_initialized--;
+	}
+	return 0;
+}
+
+#ifdef RLX
+EXPORT_SYMBOL(ali1535_smba);
+EXPORT_SYMBOL(ali1535_access);
+EXPORT_SYMBOL(i2c_ali1535_sem);
+#else
+EXPORT_NO_SYMBOLS;
+#endif
+
+#ifdef MODULE
+
+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");
+
+int init_module(void)
+{
+	return i2c_ali1535_init();
+}
+
+int cleanup_module(void)
+{
+	return ali1535_cleanup();
+}
+
+#endif				/* MODULE */
+
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-amd756.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-amd756.c	(revision 949)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-amd756.c	(revision 949)
@@ -0,0 +1,514 @@
+/*
+    amd756.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 1999 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.
+*/
+
+/* Note: we assume there can only be one AMD756, with one SMBus interface */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.h>
+
+/* AMD756 SMBus address offsets */
+#define SMB_ADDR_OFFSET        0xE0
+#define SMB_IOSIZE             16
+#define SMB_GLOBAL_STATUS      (0x0 + amd756_smba)
+#define SMB_GLOBAL_ENABLE      (0x2 + amd756_smba)
+#define SMB_HOST_ADDRESS       (0x4 + amd756_smba)
+#define SMB_HOST_DATA          (0x6 + amd756_smba)
+#define SMB_HOST_COMMAND       (0x8 + amd756_smba)
+#define SMB_HOST_BLOCK_DATA    (0x9 + amd756_smba)
+#define SMB_HAS_DATA           (0xA + amd756_smba)
+#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_smba)
+#define SMB_HAS_HOST_ADDRESS   (0xE + amd756_smba)
+#define SMB_SNOOP_ADDRESS      (0xF + amd756_smba)
+
+/* PCI Address Constants */
+
+/* address of I/O space */
+#define SMBBA     0x058		/* mh */
+
+/* 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
+
+/* insmod parameters */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_amd756_init(void);
+static int __init amd756_cleanup(void);
+static int amd756_setup(void);
+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);
+static void amd756_do_pause(unsigned int amount);
+static int amd756_transaction(void);
+static void amd756_inc(struct i2c_adapter *adapter);
+static void amd756_dec(struct i2c_adapter *adapter);
+static u32 amd756_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_access */ amd756_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ amd756_func,
+};
+
+static struct i2c_adapter amd756_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
+	&smbus_algorithm,
+	NULL,
+	amd756_inc,
+	amd756_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata amd756_initialized;
+static unsigned short amd756_smba = 0;
+
+/* externalize it */
+#define PCI_DEVICE_ID_AMD_756 0x740B
+
+/* Detect whether a AMD756 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 amd756_setup(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	struct pci_dev *AMD756_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-amd756.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the AMD756, function 3 */
+	/* Note: we keep on searching until we have found 'function 3' */
+	AMD756_dev = NULL;
+	do
+		AMD756_dev = pci_find_device(PCI_VENDOR_ID_AMD,
+					     PCI_DEVICE_ID_AMD_756,
+					     AMD756_dev);
+	while (AMD756_dev && (PCI_FUNC(AMD756_dev->devfn) != 3));
+	if (AMD756_dev == NULL) {
+		printk
+		    ("i2c-amd756.o: Error: Can't detect AMD756, function 3!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+
+	pci_read_config_byte(AMD756_dev, SMBGCFG, &temp);
+	if ((temp & 128) == 0) {
+		printk
+		    ("SMBUS: Error: Host SMBus controller I/O not enabled!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Determine the address of the SMBus areas */
+	/* Technically it is a dword but... */
+	pci_read_config_word(AMD756_dev, SMBBA, &amd756_smba);
+	amd756_smba &= 0xff00;
+	amd756_smba += SMB_ADDR_OFFSET;
+
+	if (check_region(amd756_smba, SMB_IOSIZE)) {
+		printk
+		    ("i2c-amd756.o: AMD756_smb region 0x%x already in use!\n",
+		     amd756_smba);
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(amd756_smba, SMB_IOSIZE, "amd756-smbus");
+
+#ifdef DEBUG
+	/*
+	   if ((temp & 0x0E) == 8)
+	   printk("i2c-amd756.o: AMD756 using Interrupt 9 for SMBus.\n");
+	   else if ((temp & 0x0E) == 0)
+	   printk("i2c-amd756.o: AMD756 using Interrupt SMI# for SMBus.\n");
+	   else 
+	   printk("i2c-amd756.o: AMD756: Illegal Interrupt configuration (or code out "
+	   "of date)!\n");
+	 */
+
+	pci_read_config_byte(AMD756_dev, SMBREV, &temp);
+	printk("i2c-amd756.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-amd756.o: AMD756_smba = 0x%X\n", amd756_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+/* 
+  SMBUS event = I/O 28-29 bit 11
+     see E0 for the status bits and enabled in E2
+     
+*/
+
+/* Internally used pause function */
+void amd756_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+#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_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)
+
+/* Another internally used function */
+int amd756_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-amd756.o: 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));
+#endif
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* TODO: What about SM_BSY? */
+	if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_HST_STS) != 0x00) {
+#ifdef DEBUG
+		printk
+		    ("i2c-amd756.o: SMBus busy (%04x). Resetting (NOT)... \n",
+		     temp);
+#endif
+		/* TODO: How to reset the AMD?
+		   outb_p(temp, SMBHSTSTS);
+		 */
+		if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_HST_STS) !=
+		    0x00) {
+#ifdef DEBUG
+			printk("i2c-amd756.o: Failed! (%04x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("i2c-amd756.o: Successfull!\n");
+#endif
+		}
+	}
+
+	/* start the transaction by setting bit 6 */
+	outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		amd756_do_pause(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) {
+#ifdef DEBUG
+		printk("i2c-amd756.o: SMBus explicit timeout!\n");
+		result = -1;
+#endif
+	}
+
+	if (temp & GS_PRERR_STS) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-amd756.o: SMBus Protocol error!\n");
+#endif
+	}
+
+	if (temp & GS_COL_STS) {
+		result = -1;
+		printk("i2c-amd756.o: SMBus collision!\n");
+		/* TODO: Clear Collision Status with a 1 */
+	}
+
+	if (temp & GS_TO_STS) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-amd756.o: SMBus protocol timeout!\n");
+#endif
+	}
+#ifdef DEBUG
+	if (temp & GS_HCYC_STS) {
+		printk("i2c-amd756.o: SMBus protocol success!\n");
+	}
+#endif
+
+	outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
+
+	if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
+#ifdef DEBUG
+		printk
+		    ("i2c-amd756.o: Failed reset at end of transaction (%04x)\n",
+		     temp);
+#endif
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-amd756.o: 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;
+}
+
+/* Return -1 on error. See smbus.h for more information */
+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) {
+	case I2C_SMBUS_PROC_CALL:
+		printk
+		    ("i2c-amd756.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		/* TODO: Well... It is supported, I'm just not sure what to do here... */
+		return -1;
+	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);
+		/* TODO: Why only during write? */
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMB_HOST_COMMAND);
+		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;
+	}
+
+	/* 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 & 63);
+		/* 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;
+}
+
+void amd756_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void amd756_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_amd756_init(void)
+{
+	int res;
+	printk("i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (amd756_initialized) {
+		printk
+		    ("i2c-amd756.o: Oops, amd756_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	amd756_initialized = 0;
+	if ((res = amd756_setup())) {
+		printk
+		    ("i2c-amd756.o: AMD756 not detected, module not inserted.\n");
+		amd756_cleanup();
+		return res;
+	}
+	amd756_initialized++;
+	sprintf(amd756_adapter.name, "SMBus AMD756 adapter at %04x",
+		amd756_smba);
+	if ((res = i2c_add_adapter(&amd756_adapter))) {
+		printk
+		    ("i2c-amd756.o: Adapter registration failed, module not inserted.\n");
+		amd756_cleanup();
+		return res;
+	}
+	amd756_initialized++;
+	printk("i2c-amd756.o: AMD756 bus detected and initialized\n");
+	return 0;
+}
+
+int __init amd756_cleanup(void)
+{
+	int res;
+	if (amd756_initialized >= 2) {
+		if ((res = i2c_del_adapter(&amd756_adapter))) {
+			printk
+			    ("i2c-amd756.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			amd756_initialized--;
+	}
+	if (amd756_initialized >= 1) {
+		release_region(amd756_smba, SMB_IOSIZE);
+		amd756_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
+MODULE_DESCRIPTION("AMD756 SMBus driver");
+
+int init_module(void)
+{
+	return i2c_amd756_init();
+}
+
+int cleanup_module(void)
+{
+	return amd756_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali15x3.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali15x3.c	(revision 949)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-ali15x3.c	(revision 949)
@@ -0,0 +1,635 @@
+/*
+    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 */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.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 500		/* 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 */
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_ali15x3_init(void);
+static int __init ali15x3_cleanup(void);
+static int ali15x3_setup(void);
+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);
+static void ali15x3_do_pause(unsigned int amount);
+static int ali15x3_transaction(void);
+static void ali15x3_inc(struct i2c_adapter *adapter);
+static void ali15x3_dec(struct i2c_adapter *adapter);
+static u32 ali15x3_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_access */ ali15x3_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ ali15x3_func,
+};
+
+static struct i2c_adapter ali15x3_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3,
+	&smbus_algorithm,
+	NULL,
+	ali15x3_inc,
+	ali15x3_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata ali15x3_initialized;
+static unsigned short ali15x3_smba = 0;
+
+
+/* Detect whether a ALI15X3 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 ali15x3_setup(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	struct pci_dev *ALI15X3_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-ali15x3.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the ALI15X3, M7101 device */
+	ALI15X3_dev = NULL;
+	ALI15X3_dev = pci_find_device(PCI_VENDOR_ID_AL,
+				      PCI_DEVICE_ID_AL_M7101, ALI15X3_dev);
+	if (ALI15X3_dev == NULL) {
+		printk("i2c-ali15x3.o: Error: Can't detect ali15x3!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* 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) {
+		printk
+		    ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS?\n");
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+	if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) {
+		printk
+		    ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n",
+		     ali15x3_smba);
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+/* check if whole device is enabled */
+	pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp);
+	if ((temp & 1) == 0) {
+		printk
+		    ("SMBUS: Error: SMB device not enabled - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Is SMB Host controller enabled? */
+	pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp);
+	if ((temp & 1) == 0) {
+		printk
+		    ("SMBUS: Error: Host 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(ALI15X3_dev, SMBCLK, 0x20);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb");
+
+#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-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n");
+*/
+	pci_read_config_byte(ALI15X3_dev, SMBREV, &temp);
+	printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+/* Internally used pause function */
+void ali15x3_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int ali15x3_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-ali15x3.o: 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));
+#endif
+
+	/* 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" printk.
+   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 */
+/*
+#ifdef DEBUG
+    printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp);
+#endif
+    outb_p(ALI15X3_ABORT, SMBHSTCNT);
+    temp = inb_p(SMBHSTSTS);
+    if (temp & ALI15X3_STS_BUSY) {
+*/
+
+/*
+   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.
+*/
+		printk
+		    ("i2c-ali15x3.o: 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. */
+			printk
+			    ("i2c-ali15x3.o: 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 {
+		ali15x3_do_pause(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;
+		printk("i2c-ali15x3.o: SMBus Timeout!\n");
+	}
+
+	if (temp & ALI15X3_STS_TERM) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-ali15x3.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+/*
+  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;
+#ifdef DEBUG
+		printk
+		    ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n",
+		     inb_p(SMBHSTADD));
+#endif
+	}
+
+/* haven't ever seen this */
+	if (temp & ALI15X3_STS_DEV) {
+		result = -1;
+		printk("i2c-ali15x3.o: Error: device error\n");
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-ali15x3.o: 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));
+#endif
+	return result;
+}
+
+/* Return -1 on error. See smbus.h for more information */
+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++) {
+		ali15x3_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	}
+	if (timeout >= MAX_TIMEOUT) {
+		printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n",
+		       temp);
+	}
+
+	switch (size) {
+	case I2C_SMBUS_PROC_CALL:
+		printk
+		    ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		return -1;
+	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);
+			outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = ALI15X3_BLOCK_DATA;
+		break;
+	}
+
+	outb_p(size, SMBHSTCNT);	/* output command */
+
+	if (ali15x3_transaction())	/* 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;
+		outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+#ifdef DEBUG
+			printk
+			    ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n",
+			     len, i, data->block[i]);
+#endif	/* DEBUG */
+		}
+		break;
+	}
+	return 0;
+}
+
+void ali15x3_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void ali15x3_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_ali15x3_init(void)
+{
+	int res;
+	printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (ali15x3_initialized) {
+		printk
+		    ("i2c-ali15x3.o: Oops, ali15x3_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	ali15x3_initialized = 0;
+	if ((res = ali15x3_setup())) {
+		printk
+		    ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n");
+		ali15x3_cleanup();
+		return res;
+	}
+	ali15x3_initialized++;
+	sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x",
+		ali15x3_smba);
+	if ((res = i2c_add_adapter(&ali15x3_adapter))) {
+		printk
+		    ("i2c-ali15x3.o: Adapter registration failed, module not inserted.\n");
+		ali15x3_cleanup();
+		return res;
+	}
+	ali15x3_initialized++;
+	printk
+	    ("i2c-ali15x3.o: ALI15X3 SMBus Controller detected and initialized\n");
+	return 0;
+}
+
+int __init ali15x3_cleanup(void)
+{
+	int res;
+	if (ali15x3_initialized >= 2) {
+		if ((res = i2c_del_adapter(&ali15x3_adapter))) {
+			printk
+			    ("i2c-ali15x3.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			ali15x3_initialized--;
+	}
+	if (ali15x3_initialized >= 1) {
+		release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
+		ali15x3_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("ALI15X3 SMBus driver");
+
+int init_module(void)
+{
+	return i2c_ali15x3_init();
+}
+
+int cleanup_module(void)
+{
+	return ali15x3_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-sis5595.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-sis5595.c	(revision 943)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-sis5595.c	(revision 943)
@@ -0,0 +1,447 @@
+/*
+    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 */
+
+/* TO DO: 
+ * Add Block Transfers (ugly, but supported by the adapter)
+ * Add adapter resets
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.h>
+
+/* 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 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 */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_sis5595_init(void);
+static int __init sis5595_cleanup(void);
+static int sis5595_setup(void);
+static s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
+			  unsigned short flags, char read_write,
+			  u8 command, int size,
+			  union i2c_smbus_data *data);
+static void sis5595_do_pause(unsigned int amount);
+static int sis5595_transaction(void);
+static void sis5595_inc(struct i2c_adapter *adapter);
+static void sis5595_dec(struct i2c_adapter *adapter);
+static u32 sis5595_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_access */ sis5595_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ sis5595_func,
+};
+
+static struct i2c_adapter sis5595_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595,
+	&smbus_algorithm,
+	NULL,
+	sis5595_inc,
+	sis5595_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata sis5595_initialized;
+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(void)
+{
+	int error_return = 0;
+
+	struct pci_dev *SIS5595_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-sis5595.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the SIS5595 */
+	SIS5595_dev = NULL;
+	if (!(SIS5595_dev = pci_find_device(PCI_VENDOR_ID_SI,
+					    PCI_DEVICE_ID_SI_503,
+					    SIS5595_dev))) {
+		printk("i2c-sis5595.o: Error: Can't detect SIS5595!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Determine the address of the SMBus areas */
+	pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
+	if(sis5595_base == 0) {
+		printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+#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);
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus");
+
+      END:
+	return error_return;
+}
+
+
+/* Internally used pause function */
+void sis5595_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* 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 {
+		sis5595_do_pause(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. See smbus.h for more information */
+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;
+	}
+
+	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;
+}
+
+void sis5595_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void sis5595_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_sis5595_init(void)
+{
+	int res;
+	printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (sis5595_initialized) {
+		printk
+		    ("i2c-sis5595.o: Oops, sis5595_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	sis5595_initialized = 0;
+	if ((res = sis5595_setup())) {
+		printk
+		    ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n");
+		sis5595_cleanup();
+		return res;
+	}
+	sis5595_initialized++;
+	sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
+		sis5595_base + SMB_INDEX);
+	if ((res = i2c_add_adapter(&sis5595_adapter))) {
+		printk
+		    ("i2c-sis5595.o: Adapter registration failed, module not inserted.\n");
+		sis5595_cleanup();
+		return res;
+	}
+	sis5595_initialized++;
+	printk("i2c-sis5595.o: SIS5595 bus detected and initialized\n");
+	return 0;
+}
+
+int __init sis5595_cleanup(void)
+{
+	int res;
+	if (sis5595_initialized >= 2) {
+		if ((res = i2c_del_adapter(&sis5595_adapter))) {
+			printk
+			    ("i2c-sis5595.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			sis5595_initialized--;
+	}
+	if (sis5595_initialized >= 1) {
+		release_region(sis5595_base + SMB_INDEX, 2);
+		sis5595_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("SIS5595 SMBus driver");
+
+int init_module(void)
+{
+	return i2c_sis5595_init();
+}
+
+int cleanup_module(void)
+{
+	return sis5595_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-hydra.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-hydra.c	(revision 991)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-hydra.c	(revision 991)
@@ -0,0 +1,204 @@
+/*
+    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/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/types.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/init.h>
+
+/* PCI device */
+#define VENDOR		PCI_VENDOR_ID_APPLE
+#define DEVICE		PCI_DEVICE_ID_APPLE_HYDRA
+
+#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);
+}
+
+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);
+}
+
+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)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void bit_hydra_dec(struct i2c_adapter *adap)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct i2c_algo_bit_data bit_hydra_data = {
+	NULL,
+	bit_hydra_setsda,
+	bit_hydra_setscl,
+	bit_hydra_getsda,
+	bit_hydra_getscl,
+	5, 5, 100,		/*waits, timeout */
+};
+
+static struct i2c_adapter bit_hydra_ops = {
+	"Hydra i2c",
+	I2C_HW_B_HYDRA,
+	NULL,
+	&bit_hydra_data,
+	bit_hydra_inc,
+	bit_hydra_dec,
+	NULL,
+	NULL,
+};
+
+
+static int find_hydra(void)
+{
+	struct pci_dev *dev;
+	unsigned int base_addr;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	dev = pci_find_device(VENDOR, DEVICE, NULL);
+	if (!dev) {
+		printk("Hydra not found\n");
+		return -ENODEV;
+	}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
+	base_addr = dev->resource[0].start;
+#else
+	base_addr = dev->base_address[0];
+#endif
+	hydra_base = (unsigned long) ioremap(base_addr, 0x100);
+
+	return 0;
+}
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_hydra_init(void)
+{
+	if (find_hydra() < 0) {
+		printk("Error while reading PCI configuration\n");
+		return -ENODEV;
+	}
+
+	pdregw(0);		/* clear SCLK_OE and SDAT_OE */
+
+	if (i2c_bit_add_bus(&bit_hydra_ops) == 0) {
+		printk("Hydra i2c: Module succesfully loaded\n");
+		return 0;
+	} else {
+		iounmap((void *) hydra_base);
+		printk
+		    ("Hydra i2c: Algo-bit error, couldn't register bus\n");
+		return -ENODEV;
+	}
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
+
+int init_module(void)
+{
+	return i2c_hydra_init();
+}
+
+void cleanup_module(void)
+{
+	i2c_bit_del_bus(&bit_hydra_ops);
+	if (hydra_base) {
+		pdregw(0);	/* clear SCLK_OE and SDAT_OE */
+		iounmap((void *) hydra_base);
+	}
+}
+#endif
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-voodoo3.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-voodoo3.c	(revision 751)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-voodoo3.c	(revision 751)
@@ -0,0 +1,359 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "version.h"
+#include <linux/init.h>
+
+/* 3DFX defines */
+#ifndef PCI_DEVICE_ID_3DFX_VOODOO3
+#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05
+#endif
+#ifndef PCI_DEVICE_ID_3DFX_BANSHEE
+#define PCI_DEVICE_ID_3DFX_BANSHEE 0x03
+#endif
+
+/* 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		50
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_voodoo3_init(void);
+static int __init voodoo3_cleanup(void);
+static int voodoo3_setup(void);
+static void config_v3(struct pci_dev *dev);
+static void voodoo3_inc(struct i2c_adapter *adapter);
+static void voodoo3_dec(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+
+static int __initdata voodoo3_initialized;
+static unsigned char *mem;
+
+extern inline void outlong(unsigned int dat)
+{
+	*((unsigned int *) (mem + REG)) = dat;
+}
+
+extern inline unsigned int readlong(void)
+{
+	return *((unsigned int *) (mem + REG));
+}
+
+/* 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 = readlong();
+	if(val)
+		r |= I2C_SCL_OUT;
+	else
+		r &= ~I2C_SCL_OUT;
+	outlong(r);
+}
+
+static void bit_vooi2c_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readlong();
+	if(val)
+		r |= I2C_SDA_OUT;
+	else
+		r &= ~I2C_SDA_OUT;
+	outlong(r);
+}
+
+/* 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 != (readlong() & I2C_SCL_IN));
+}
+
+static int bit_vooi2c_getsda(void *data)
+{
+	return (0 != (readlong() & I2C_SDA_IN));
+}
+
+static void bit_vooddc_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readlong();
+	if(val)
+		r |= DDC_SCL_OUT;
+	else
+		r &= ~DDC_SCL_OUT;
+	outlong(r);
+}
+
+static void bit_vooddc_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readlong();
+	if(val)
+		r |= DDC_SDA_OUT;
+	else
+		r &= ~DDC_SDA_OUT;
+	outlong(r);
+}
+
+static int bit_vooddc_getscl(void *data)
+{
+	return (0 != (readlong() & DDC_SCL_IN));
+}
+
+static int bit_vooddc_getsda(void *data)
+{
+	return (0 != (readlong() & DDC_SDA_IN));
+}
+
+static struct i2c_algo_bit_data voo_i2c_bit_data = {
+	NULL,
+	bit_vooi2c_setsda,
+	bit_vooi2c_setscl,
+	bit_vooi2c_getsda,
+	bit_vooi2c_getscl,
+	CYCLE_DELAY, CYCLE_DELAY, TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_i2c_adapter = {
+	"I2C Voodoo3/Banshee adapter",
+	I2C_HW_B_VOO,
+	NULL,
+	&voo_i2c_bit_data,
+	voodoo3_inc,
+	voodoo3_dec,
+	NULL,
+	NULL,
+};
+
+static struct i2c_algo_bit_data voo_ddc_bit_data = {
+	NULL,
+	bit_vooddc_setsda,
+	bit_vooddc_setscl,
+	bit_vooddc_getsda,
+	bit_vooddc_getscl,
+	CYCLE_DELAY, CYCLE_DELAY, TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_ddc_adapter = {
+	"DDC Voodoo3/Banshee adapter",
+	I2C_HW_B_VOO,
+	NULL,
+	&voo_ddc_bit_data,
+	voodoo3_inc,
+	voodoo3_dec,
+	NULL,
+	NULL,
+};
+
+/* Configures the chip */
+
+void config_v3(struct pci_dev *dev)
+{
+	unsigned int cadr;
+
+	/* map Voodoo3 memory */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
+	cadr = dev->resource[0].start;
+#else
+	cadr = dev->base_address[0];
+#endif
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	mem = ioremap_nocache(cadr, 0x1000);
+
+	*((unsigned int *) (mem + REG2)) = 0x8160;
+	*((unsigned int *) (mem + REG)) = 0xcffc0020;
+	printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%p\n", mem);
+}
+
+/* Detect whether a Voodoo3 or a Banshee can be found,
+   and initialize it. */
+static int voodoo3_setup(void)
+{
+	struct pci_dev *dev;
+	int v3_num;
+
+	v3_num = 0;
+
+	dev = NULL;
+	do {
+		if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX,
+					   PCI_DEVICE_ID_3DFX_VOODOO3,
+					   dev))) {
+			if (!v3_num)
+				config_v3(dev);
+			v3_num++;
+		}
+	} while (dev);
+
+	dev = NULL;
+	do {
+		if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX,
+					   PCI_DEVICE_ID_3DFX_BANSHEE,
+					   dev))) {
+			if (!v3_num)
+				config_v3(dev);
+			v3_num++;
+		}
+	} while (dev);
+
+	if (v3_num > 0) {
+		printk(KERN_INFO
+		       "i2c-voodoo3: %d Banshee/Voodoo3 found.\n", v3_num);
+		if (v3_num > 1)
+			printk(KERN_INFO
+			       "i2c-voodoo3: warning: only 1 supported.\n");
+		return 0;
+	} else {
+		printk(KERN_INFO "i2c-voodoo3: No Voodoo3 found.\n");
+		return -ENODEV;
+	}
+}
+
+void voodoo3_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void voodoo3_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+int __init i2c_voodoo3_init(void)
+{
+	int res;
+	printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	voodoo3_initialized = 0;
+	if ((res = voodoo3_setup())) {
+		printk
+		    ("i2c-voodoo3.o: Voodoo3 not detected, module not inserted.\n");
+		voodoo3_cleanup();
+		return res;
+	}
+	if ((res = i2c_bit_add_bus(&voodoo3_i2c_adapter))) {
+		printk("i2c-voodoo3.o: I2C adapter registration failed\n");
+	} else {
+		printk("i2c-voodoo3.o: I2C bus initialized\n");
+		voodoo3_initialized |= INIT2;
+	}
+	if ((res = i2c_bit_add_bus(&voodoo3_ddc_adapter))) {
+		printk("i2c-voodoo3.o: DDC adapter registration failed\n");
+	} else {
+		printk("i2c-voodoo3.o: DDC bus initialized\n");
+		voodoo3_initialized |= INIT3;
+	}
+	if(!(voodoo3_initialized & (INIT2 | INIT3))) {
+		printk("i2c-voodoo3.o: Both registrations failed, module not inserted\n");
+		voodoo3_cleanup();
+		return res;
+	}
+	return 0;
+}
+
+int __init voodoo3_cleanup(void)
+{
+	int res;
+
+	iounmap(mem);
+	if (voodoo3_initialized & INIT3) {
+		if ((res = i2c_bit_del_bus(&voodoo3_ddc_adapter))) {
+			printk
+			    ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n");
+			return res;
+		}
+	}
+	if (voodoo3_initialized & INIT2) {
+		if ((res = i2c_bit_del_bus(&voodoo3_i2c_adapter))) {
+			printk
+			    ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n");
+			return res;
+		}
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+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");
+
+
+int init_module(void)
+{
+	return i2c_voodoo3_init();
+}
+
+int cleanup_module(void)
+{
+	return voodoo3_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-keywest.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-keywest.c	(revision 816)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-keywest.c	(revision 816)
@@ -0,0 +1,459 @@
+/*
+    i2c-keywest.c - Part of lm_sensors,  Linux kernel modules
+                for hardware monitoring
+
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    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.
+*/
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include <linux/i2c.h>
+
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/prom.h>
+// #include <asm/dbdma.h>
+// #include <asm/cuda.h>
+// #include <asm/pmu.h>
+#include <asm/feature.h>
+#include <linux/nvram.h>
+// #include <linux/vt_kern.h>
+
+
+#define POLL_SANITY 100 
+
+/* PCI device */
+#define VENDOR		0x106b
+#define DEVICE		0x22
+
+/*****    Protos    ******/
+s32 keywest_access(struct i2c_adapter * adap, u16 addr,
+                  unsigned short flags, char read_write,
+                  u8 command, int size, union i2c_smbus_data * data);
+u32 keywest_func(struct i2c_adapter *adapter);
+/**/
+
+struct keywest_iface {
+	void *base;
+	void *steps;
+	void *mode;
+	void *control;
+	void *status;
+	void *ISR;
+	void *IER;
+	void *addr;
+	void *subaddr;
+	void *data;
+	struct i2c_adapter *i2c_adapt;
+	struct keywest_iface *next;
+};
+
+static struct i2c_algorithm smbus_algorithm = {
+        /* name */ "Non-I2C SMBus adapter",
+        /* id */ I2C_ALGO_SMBUS,
+        /* master_xfer */ NULL,
+        /* smbus_access */ keywest_access,
+        /* slave_send */ NULL,
+        /* slave_rcv */ NULL,
+        /* algo_control */ NULL,
+        /* functionality */ keywest_func,
+};
+
+void dump_ifaces (struct keywest_iface **ifaces);
+/***** End of Protos ******/
+
+
+/** Vars **/
+struct keywest_iface *ifaces=NULL;
+
+
+/** Functions **/
+
+int poll_interrupt(void *ISR) {
+ int i,res;
+  for (i=0; i < POLL_SANITY; i++) {
+    udelay(100);
+    if (res=readb(ISR) & 0x0F > 0) { /*printk("i2c-keywest: received interrupt: 0x%02X\n",res);*/ return res; }
+  }
+  if (i == POLL_SANITY) { printk("i2c-keywest: Sanity check failed!  Expected interrupt never happened.\n"); return -1; }
+  return -1; /* Should never get here */
+}
+
+
+void keywest_reset(struct keywest_iface *ifaceptr) {
+int i;
+int interrupt_state=1;
+
+  /* Let's see if we can clear some busy conditions */
+//  writeb(0xFF,ifaceptr->status);
+  writeb(0x04,ifaceptr->control);
+  writeb(0xFF,ifaceptr->ISR);
+//  printk("Initalizing.  Ignore: ");
+  for (i=0;(i<10) & (interrupt_state > 0);i++) {
+   interrupt_state=poll_interrupt(ifaceptr->ISR);
+   if (interrupt_state) writeb(interrupt_state,ifaceptr->ISR);
+  }
+}
+
+s32 keywest_access(struct i2c_adapter * adap, u16 addr,
+                  unsigned short flags, char read_write,
+                  u8 command, int size, union i2c_smbus_data * data) {
+
+ struct keywest_iface *ifaceptr;
+ int interrupt_state=1;
+ int ack;
+ int error_state=0;
+ int len,i; /* for block transfers */
+
+  ifaceptr=(struct keywest_iface *)adap->data;
+//  printk("Keywest access called with ref to iface: 0x%X\n",ifaceptr->base);
+//  printk("Access request: addr:%X flags:%X rw:%X command:%X size:%X\n",
+//   addr, flags, read_write, command, size);
+
+  keywest_reset(ifaceptr);
+  printk("start of transaction\n");
+  dump_ifaces(&ifaces);
+
+  /* see if bus is busy */
+  if ((readb(ifaceptr->status) & 1) > 0) {
+   printk("i2c-keywest: Busy!\n");
+   dump_ifaces(&ifaces);
+   return -1;
+  }
+  /* Set up address and r/w bit */
+  writeb(((addr << 1) | (read_write == I2C_SMBUS_READ)),(void *)ifaceptr->addr);
+  /* Set up 'sub address' which I'm guessing is the command field? */
+  writeb(command,(void *)ifaceptr->subaddr);
+  /* Start sending address */
+  writeb(readb(ifaceptr->control) | 2,ifaceptr->control);
+  interrupt_state=poll_interrupt(ifaceptr->ISR);
+  ack=readb(ifaceptr->status) & 0x0F;
+//  printk("status:0x%02X ack:0x%02X\n",ack,interrupt_state);
+  if ((ack & 0x02)==0) {
+   printk("i2c-keywest: Ack Status on addr expected but got: 0x%02X on addr: 0x%02X\n",ack,addr);
+   return -1;
+  } else {  
+  if (size & (I2C_FUNC_SMBUS_BYTE_DATA | 
+      I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)) {
+   if (read_write == I2C_SMBUS_READ)
+    writeb(1 | readb(ifaceptr->control),ifaceptr->control); /* Set ACK if reading */
+   switch (size) {
+    case I2C_SMBUS_BYTE_DATA:
+     if (read_write == I2C_SMBUS_WRITE) {
+       writeb(data->byte,ifaceptr->data);
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(2)!\n"); error_state=-1; } 
+       writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */
+     } else {
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       data->byte=readb(ifaceptr->data);
+       writeb(0,ifaceptr->control); /* End read: clear ack */
+     }
+     break;
+    case I2C_SMBUS_WORD_DATA:
+     if (read_write == I2C_SMBUS_WRITE) {
+       writeb(data->word & 0x0ff,ifaceptr->data);
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(2)!\n"); error_state=-1; } 
+       writeb((data->word & 0x0ff00) >> 8,ifaceptr->data);
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(3)!\n"); error_state=-1; } 
+       writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */
+     } else {
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       data->word=(readb(ifaceptr->data) << 8);
+       writeb(1,ifaceptr->control); /* Send ack */
+       writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+       interrupt_state=poll_interrupt(ifaceptr->ISR);
+       data->word|=(readb(ifaceptr->data));
+       writeb(0,ifaceptr->control); /* End read: clear ack */
+     }
+     break;
+    case I2C_SMBUS_BLOCK_DATA:
+     if (read_write == I2C_SMBUS_WRITE) {
+       len = data->block[0];
+       if (len < 0) len = 0;
+       if (len > 32) len = 32;
+       for (i = 1; i <= len; i++) {
+        writeb(data->byte,ifaceptr->data);
+        writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+        interrupt_state=poll_interrupt(ifaceptr->ISR);
+        if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(block)!\n"); error_state=-1; } 
+       }
+       writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */
+     } else {
+       for (i = 1; i <= data->block[0]; i++) {
+         writeb(1,ifaceptr->control); /* Send ack */
+         writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+         interrupt_state=poll_interrupt(ifaceptr->ISR);
+         data->block[i] =readb(ifaceptr->data);
+       }
+     }
+     break;
+   } /* switch */
+  } /* if size */
+  } /* if ack */
+  writeb(0,ifaceptr->control); /* End read: clear ack */
+  writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */
+  writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */
+  if (((interrupt_state=poll_interrupt(ifaceptr->ISR)) & 0x04) > 0)
+    printk("i2c-keywest: Expected stop interrupt but got: 0x%02X\n",interrupt_state);
+    error_state=-1;
+  writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt*/
+  printk("Transaction complete.\n");
+  dump_ifaces(&ifaces);
+  return error_state;
+}
+
+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;
+}
+
+void keywest_inc(struct i2c_adapter *adapter)
+{
+        MOD_INC_USE_COUNT;
+}
+
+void keywest_dec(struct i2c_adapter *adapter)
+{
+        MOD_DEC_USE_COUNT;
+}
+
+/**** Internal Functions ****/
+
+int init_iface (void *base,void *steps,struct keywest_iface **ifaces) {
+ struct i2c_adapter *keywest_adapter;
+ char *name;
+ struct keywest_iface **temp_hdl=ifaces;
+ int res,temp;
+
+  if (ifaces == NULL) {
+   printk("Ah!  Passed a null handle to init_iface");
+   return -1;
+  }
+  while ((*temp_hdl) != NULL) {
+   printk("found an entry, skipping.\n");
+   temp_hdl=&(*temp_hdl)->next;
+  }
+  *temp_hdl=(struct keywest_iface *)kmalloc(sizeof(struct keywest_iface),GFP_KERNEL);
+  if (*temp_hdl == NULL) { printk("kmalloc failed on temp_hdl!"); return -1; }
+  (*temp_hdl)->next=NULL;
+  base=ioremap((int)base,(int)steps*8);
+  (*temp_hdl)->base=base;
+  (*temp_hdl)->steps=steps;
+  (*temp_hdl)->mode=(void *)base;
+  (*temp_hdl)->control=(void *)((__u32)base+(__u32)steps);
+  (*temp_hdl)->status=(void *)((__u32)base+(__u32)steps*2);
+  (*temp_hdl)->ISR=(void *)((__u32)base+(__u32)steps*3);
+  (*temp_hdl)->IER=(void *)((__u32)base+(__u32)steps*4);
+  (*temp_hdl)->addr=(void *)((__u32)base+(__u32)steps*5);
+  (*temp_hdl)->subaddr=(void *)((__u32)base+(__u32)steps*6);
+  (*temp_hdl)->data=(void *)((__u32)base+(__u32)steps*7);
+  keywest_adapter=(struct i2c_adapter*)kmalloc(sizeof(struct i2c_adapter),GFP_KERNEL);
+  memset((void *)keywest_adapter,0,(sizeof(struct i2c_adapter)));
+  if (keywest_adapter == NULL) { printk("kmalloc failed on keywest_adapter!"); return -1; }
+  strcpy(keywest_adapter->name,"keywest i2c\0");
+  keywest_adapter->id=I2C_ALGO_SMBUS;
+  keywest_adapter->algo=&smbus_algorithm;
+  keywest_adapter->algo_data=NULL;
+  keywest_adapter->inc_use=keywest_inc;
+  keywest_adapter->dec_use=keywest_dec;
+  keywest_adapter->client_register=NULL;
+  keywest_adapter->client_unregister=NULL;
+  keywest_adapter->data=(void *)(*temp_hdl);
+  (*temp_hdl)->i2c_adapt=keywest_adapter;
+  if (res=i2c_add_adapter((*temp_hdl)->i2c_adapt)) {
+                printk
+                    ("i2c-keywest.o: Adapter registration failed, module not inserted.\n");
+                cleanup();
+  }
+  /* Now actually do the initialization of the device */
+  writeb(0x08,(*temp_hdl)->mode); /* So-called 'standard sub-mode' */
+  writeb(0,(*temp_hdl)->status);
+  writeb(0x0FF,(*temp_hdl)->ISR);
+  printk("Done creating iface entry\n");
+
+  keywest_reset(*temp_hdl);
+
+// Test access to SDA and SCK
+  printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status));
+  writeb( 0x10,(*temp_hdl)->control);
+  temp=readb((*temp_hdl)->control);
+  printk("Serial settings: %02X\n",temp);
+  udelay(10);
+  keywest_reset(*temp_hdl);
+  
+  writeb(0x10,(*temp_hdl)->control);
+  temp=readb((*temp_hdl)->control);
+  printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status));
+  udelay(10);
+  keywest_reset(*temp_hdl);
+
+  writeb(0x08,(*temp_hdl)->control);
+  temp=readb((*temp_hdl)->control);
+  printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status));
+  udelay(10);
+  keywest_reset(*temp_hdl);
+
+  writeb(0x08,(*temp_hdl)->control);
+  temp=readb((*temp_hdl)->control);
+  printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status));
+
+  return res;
+}
+
+void dump_ifaces (struct keywest_iface **ifaces) {
+  if (ifaces == NULL) { printk("Ah!  Passed null handle to dump!\n"); return; }
+  if (*ifaces != NULL) {
+   printk("Interface @%X,%X  Locations:\n",(*ifaces)->base,(*ifaces)->steps);
+   printk("  mode:%X control:%X status:%X ISR:%X IER:%X addr:%X subaddr:%X data:%X\n",
+     (*ifaces)->mode,(*ifaces)->control,(*ifaces)->status,(*ifaces)->ISR,
+     (*ifaces)->IER,(*ifaces)->addr,(*ifaces)->subaddr,(*ifaces)->data);
+   printk("Contents:\n");
+   printk("  mode:0x%02X control: 0x%02X status:0x%02X ISR:0x%02X IER:0x%02X addr:0x%02X subaddr:0x%02X data:0x%02X\n",
+     readb((*ifaces)->mode),readb((*ifaces)->control),readb((*ifaces)->status),readb((*ifaces)->ISR),
+     readb((*ifaces)->IER),readb((*ifaces)->addr),readb((*ifaces)->subaddr),
+     readb((*ifaces)->data));
+   printk("I2C-Adapter:\n");
+   printk("  name:%s\n",(*ifaces)->i2c_adapt->name);
+   dump_ifaces(&(*ifaces)->next);
+  } else { printk("End of ifaces.\n"); }
+}
+
+int cleanup (struct keywest_iface **ifaces) {
+ int res=0;
+
+  if (ifaces == NULL) { printk("Ah!  Passed null handle to cleanup!\n"); return; }
+  if (*ifaces != NULL) {
+   printk("Cleaning up interface @%X,%X\n",(*ifaces)->base,(*ifaces)->steps);
+   cleanup(&(*ifaces)->next);
+   if (res = i2c_del_adapter((*ifaces)->i2c_adapt)) {
+     printk ("i2c-keywest.o: i2c_del_adapter failed, module not removed\n");
+   }
+   kfree(*ifaces);
+   iounmap((*ifaces)->base);
+   (*ifaces)=NULL;
+  }
+  return res;
+}
+
+
+static void scan_of(char *dev_type) {
+ struct device_node *np=NULL;
+ struct property *dp=NULL;
+ int i;
+
+ np = find_devices(dev_type);
+ if (np == 0) { printk("No %s devices found.\n",dev_type); }
+ while (np != 0 ) {
+  printk("%s found: %s, with properties:\n",dev_type,np->full_name);
+  dp=np->properties;
+  while (dp != 0) {
+   printk("     %s = %s [ ",dp->name,dp->value);
+   for (i=0; i < dp->length; i++) {
+    printk("%02X",(char)dp->value[i]);
+    if (((i+1) % 4) == 0) { printk(" "); }
+   }
+   printk("] (length=%d)\n",dp->length);
+   dp=dp->next;
+  }
+  np=np->next;
+ }
+}
+
+static int find_keywest(void)
+{
+	struct pci_dev *s_bridge;
+	u8 rev;
+	struct device_node *i2c_device;
+	struct device_node *np;
+	struct property *dp;
+	void **temp;
+	int i;
+	void *base=NULL;
+	void *steps=NULL;
+
+#ifdef DEBUG
+		scan_of("i2c");
+#endif
+//		scan_of("backlight");
+//		scan_of("sound");
+//		scan_of("ethernet");
+		scan_of("i2c");
+                i2c_device = find_compatible_devices("i2c","keywest");
+		if (i2c_device == 0) { printk("No Keywest i2c devices found.\n");  return -1; }
+                while (i2c_device != 0) {
+                        printk("Keywest device found: %s\n",i2c_device->full_name);
+                        temp=(void **)get_property(i2c_device, "AAPL,address", NULL);
+			if (temp != NULL) {base=*temp;} else {printk("no 'address' prop!\n");}
+//			printk("address= [%X]\n",base);
+                        temp=(void **)get_property(i2c_device, "AAPL,address-step", NULL);
+			if (temp != NULL) {steps=*temp;} else {printk("no 'address-step' prop!\n");}
+			printk("Device found. base: %X steps: %X\n",base,steps);
+			if (init_iface(base,steps,&ifaces) !=0 ) {
+				cleanup(&ifaces); return -ENODEV;
+			}
+			i2c_device=i2c_device->next;
+		}
+	if (ifaces == NULL) { printk("ifaces null! :'("); }
+	dump_ifaces(&ifaces);
+	return 0;
+}
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_keywest_init(void)
+{
+	return find_keywest();
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+
+int init_module(void)
+{
+	return i2c_keywest_init();
+}
+
+void cleanup_module(void)
+{
+	cleanup(&ifaces);
+}
+#endif
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-isa.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-isa.c	(revision 707)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-isa.c	(revision 707)
@@ -0,0 +1,157 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/i2c.h>
+
+
+#include <linux/init.h>
+
+#include "version.h"
+#include "i2c-isa.h"
+
+static void isa_inc_use(struct i2c_adapter *adapter);
+static void isa_dec_use(struct i2c_adapter *adapter);
+static u32 isa_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_isa_init(void);
+static int __init isa_cleanup(void);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* This is the actual algorithm we define */
+static struct i2c_algorithm isa_algorithm = {
+	/* name */ "ISA bus algorithm",
+	/* id */ I2C_ALGO_ISA,
+	/* master_xfer */ NULL,
+	/* smbus_access */ NULL,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ &isa_func,
+};
+
+/* There can only be one... */
+static struct i2c_adapter isa_adapter = {
+	/* name */ "ISA main adapter",
+	/* id */ I2C_ALGO_ISA | I2C_HW_ISA,
+	/* algorithm */ &isa_algorithm,
+	/* algo_data */ NULL,
+	/* inc_use */ &isa_inc_use,
+	/* dec_use */ &isa_dec_use,
+	/* data */ NULL,
+	/* Other fields not initialized */
+};
+
+/* Used in isa_init/cleanup */
+static int __initdata isa_initialized;
+
+void isa_inc_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void isa_dec_use(struct i2c_adapter *adapter)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* We can't do a thing... */
+static u32 isa_func(struct i2c_adapter *adapter)
+{
+	return 0;
+}
+
+int __init i2c_isa_init(void)
+{
+	int res;
+	printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+	if (isa_initialized) {
+		printk
+		    ("i2c-isa.o: Oops, isa_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	isa_initialized = 0;
+	if ((res = i2c_add_adapter(&isa_adapter))) {
+		printk("i2c-isa.o: Adapter registration failed, "
+		       "module i2c-isa.o is not inserted\n.");
+		isa_cleanup();
+		return res;
+	}
+	isa_initialized++;
+	printk("i2c-isa.o: ISA bus access for i2c modules initialized.\n");
+	return 0;
+}
+
+int __init isa_cleanup(void)
+{
+	int res;
+	if (isa_initialized >= 1) {
+		if ((res = i2c_del_adapter(&isa_adapter))) {
+			printk
+			    ("i2c-isa.o: Adapter deregistration failed, module not removed.\n");
+			return res;
+		} else
+			isa_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("ISA bus access through i2c");
+
+int init_module(void)
+{
+	return i2c_isa_init();
+}
+
+int cleanup_module(void)
+{
+	return isa_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-piix4.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-piix4.c	(revision 963)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-piix4.c	(revision 963)
@@ -0,0 +1,534 @@
+/*
+    piix4.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 PIIX4, with one SMBus interface */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.h>
+
+/* Note: Since the ServerWorks OSB4 SMBus host interface is identical
+         to the Intel PIIX4's, we only mention it during detection.   */
+
+#ifndef PCI_DEVICE_ID_SERVERWORKS_OSB4
+#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200
+#endif
+
+#ifndef PCI_VENDOR_ID_SERVERWORKS
+#define PCI_VENDOR_ID_SERVERWORKS 0x01166
+#endif
+
+
+/* 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)
+
+/* 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!");
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_piix4_init(void);
+static int __init piix4_cleanup(void);
+static int piix4_setup(void);
+static s32 piix4_access(struct i2c_adapter *adap, u16 addr,
+			unsigned short flags, char read_write,
+			u8 command, int size, union i2c_smbus_data *data);
+static void piix4_do_pause(unsigned int amount);
+static int piix4_transaction(void);
+static void piix4_inc(struct i2c_adapter *adapter);
+static void piix4_dec(struct i2c_adapter *adapter);
+static u32 piix4_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_access */ piix4_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ piix4_func,
+};
+
+static struct i2c_adapter piix4_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4,
+	&smbus_algorithm,
+	NULL,
+	piix4_inc,
+	piix4_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata piix4_initialized;
+static unsigned short piix4_smba = 0;
+static kind = 0;
+
+/* 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. */
+int piix4_setup(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	struct pci_dev *PIIX4_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-piix4.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the PIIX4, function 3 or Serverworks PSB4 func 0          */
+	/* Note: we keep on searching until we have found the proper function */
+	PIIX4_dev = NULL;
+	PIIX4_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+				    PCI_DEVICE_ID_SERVERWORKS_OSB4,
+				    PIIX4_dev);
+	while (PIIX4_dev && (PCI_FUNC(PIIX4_dev->devfn) != 0));
+	if (PIIX4_dev == NULL) {
+		PIIX4_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					    PCI_DEVICE_ID_INTEL_82371AB_3,
+					    PIIX4_dev);
+		while (PIIX4_dev && (PCI_FUNC(PIIX4_dev->devfn) != 3));
+		if (PIIX4_dev == NULL) {
+		 printk
+		  ("i2c-piix4.o: Error: Can't detect PIIX4 function 3 nor OSB4 function 0!\n");
+		 error_return = -ENODEV;
+		 goto END;
+		} else { kind=1; /* Intel PIIX4 found */ }
+	} else { kind=2; /* Serverworks OSB4 found */ }
+
+
+/* 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 (check_region(piix4_smba, 8)) {
+		printk
+		    ("i2c-piix4.o: PIIX4_smb region 0x%x already in use!\n",
+		     piix4_smba);
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	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
+		    ("i2c-piix4.o: WARNING: PIIX4 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
+			    ("i2c-piix4.o: WARNING: PIIX4 SMBus interface has been FORCEFULLY "
+			     "ENABLED!\n");
+		} else {
+			printk
+			    ("SMBUS: Error: Host SMBus controller not enabled!\n");
+			error_return = -ENODEV;
+			goto END;
+		}
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(piix4_smba, 8, kind==1?"piix4-smbus":"osb4-smbus");
+
+#ifdef DEBUG
+	if ((temp & 0x0E) == 8)
+		printk
+		    ("i2c-piix4.o: PIIX4 using Interrupt 9 for SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		printk
+		    ("i2c-piix4.o: PIIX4 using Interrupt SMI# for SMBus.\n");
+	else
+		printk
+		    ("i2c-piix4.o: PIIX4: Illegal Interrupt configuration (or code out "
+		     "of date)!\n");
+
+	pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
+	printk("i2c-piix4.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-piix4.o: PIIX4_smba = 0x%X\n", piix4_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+/* Internally used pause function */
+void piix4_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int piix4_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("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("i2c-piix4.o: SMBus busy (%02x). Resetting... \n",
+		       temp);
+#endif
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+			printk("i2c-piix4.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("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 {
+		piix4_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+#ifdef DEBUG
+		printk("i2c-piix4.o: SMBus Timeout!\n");
+		result = -1;
+#endif
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-piix4.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		printk
+		    ("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("i2c-piix4.o: Error: no response!\n");
+#endif
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+		printk
+		    ("i2c-piix4.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+#endif
+	}
+#ifdef DEBUG
+	printk
+	    ("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. See smbus.h for more information */
+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_PROC_CALL:
+		printk
+		    ("i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		return -1;
+	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;
+	}
+
+	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;
+}
+
+void piix4_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void piix4_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_piix4_init(void)
+{
+	int res;
+	printk("piix4.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	if (piix4_initialized) {
+		printk
+		    ("i2c-piix4.o: Oops, piix4_init called a second time!\n");
+		return -EBUSY;
+	}
+	piix4_initialized = 0;
+	if ((res = piix4_setup())) {
+		printk
+		    ("i2c-piix4.o: PIIX4 not detected, module not inserted.\n");
+		piix4_cleanup();
+		return res;
+	}
+	piix4_initialized++;
+	sprintf(piix4_adapter.name, kind==1?"SMBus PIIX4 adapter at %04x":"SMBus OSB4 adapter at %04x",
+		piix4_smba);
+	if ((res = i2c_add_adapter(&piix4_adapter))) {
+		printk
+		    ("i2c-piix4.o: Adapter registration failed, module not inserted.\n");
+		piix4_cleanup();
+		return res;
+	}
+	piix4_initialized++;
+	printk("i2c-piix4.o: PIIX4 bus detected and initialized\n");
+	return 0;
+}
+
+int __init piix4_cleanup(void)
+{
+	int res;
+	if (piix4_initialized >= 2) {
+		if ((res = i2c_del_adapter(&piix4_adapter))) {
+			printk
+			    ("i2c-piix4.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			piix4_initialized--;
+	}
+	if (piix4_initialized >= 1) {
+		release_region(piix4_smba, 8);
+		piix4_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("PIIX4 SMBus driver");
+
+int init_module(void)
+{
+	return i2c_piix4_init();
+}
+
+int cleanup_module(void)
+{
+	return piix4_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/Module.mk
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/Module.mk	(revision 964)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/Module.mk	(revision 964)
@@ -0,0 +1,65 @@
+#  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...
+KERNELBUSSESTARGETS := $(MODULE_DIR)/i2c-i801.o \
+                       $(MODULE_DIR)/i2c-viapro.o \
+                       $(MODULE_DIR)/i2c-voodoo3.o \
+                       $(MODULE_DIR)/i2c-amd756.o \
+                       $(MODULE_DIR)/i2c-i810.o \
+                       $(MODULE_DIR)/i2c-ali1535.o \
+                       $(MODULE_DIR)/i2c-sis5595.o
+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_HYDRA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-hydra.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_PIIX4=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-piix4.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_I2C_VIA=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-via.o
+endif
+
+# Include all dependency files
+INCLUDEFILES += $(KERNELBUSSESTARGETS:.o=.d)
+
+all-kernel-busses: $(KERNELBUSSESTARGETS)
+all :: all-kernel-busses
+
+install-kernel-busses: all-kernel-busses
+	if [ -n "$(KERNELBUSSESTARGETS)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(MODDIR) ; \
+	  $(INSTALL) -o root -g root -m 644 $(KERNELBUSSESTARGETS) $(DESTDIR)$(MODDIR) ; \
+	fi
+
+install :: install-kernel-busses
+
+clean-kernel-busses:
+	$(RM) $(KERNELBUSSESDIR)/*.o $(KERNELBUSSESDIR)/*.d
+clean :: clean-kernel-busses
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-via.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-via.c	(revision 707)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-via.c	(revision 707)
@@ -0,0 +1,214 @@
+/*
+    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/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/init.h>
+
+/* PCI device */
+#define VENDOR		PCI_VENDOR_ID_VIA
+#define DEVICE		PCI_DEVICE_ID_VIA_82C586_3
+
+/* 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"
+
+/* ----- global defines -----------------------------------------------	*/
+#define DEB(x) x		/* silicon revision, io addresses               */
+#define DEB2(x) x		/* line status                                  */
+#define DEBE(x)			/*                                              */
+
+/* ----- local functions ----------------------------------------------	*/
+
+static u16 pm_io_base;
+
+static void bit_via_setscl(void *data, int state)
+{
+	outb(state ? inb(I2C_OUT) | I2C_SCL : inb(I2C_OUT) & ~I2C_SCL,
+	     I2C_OUT);
+}
+
+static void bit_via_setsda(void *data, int state)
+{
+	outb(state ? inb(I2C_OUT) | I2C_SDA : inb(I2C_OUT) & ~I2C_SDA,
+	     I2C_OUT);
+}
+
+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)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void bit_via_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct i2c_algo_bit_data bit_data = {
+	NULL,
+	bit_via_setsda,
+	bit_via_setscl,
+	bit_via_getsda,
+	bit_via_getscl,
+	5, 5, 100,		/*waits, timeout */
+};
+
+static struct i2c_adapter bit_via_ops = {
+	"VIA i2c",
+	I2C_HW_B_VIA,
+	NULL,
+	&bit_data,
+	bit_via_inc,
+	bit_via_dec,
+	NULL,
+	NULL,
+};
+
+
+/* When exactly was the new pci interface introduced? */
+static int find_via(void)
+{
+	struct pci_dev *s_bridge;
+	u16 base;
+	u8 rev;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	s_bridge = pci_find_device(VENDOR, DEVICE, NULL);
+
+	if (!s_bridge) {
+		printk("vt82c586b not found\n");
+		return -ENODEV;
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(s_bridge, PM_CFG_REVID, &rev))
+		return -ENODEV;
+
+	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 */
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, base, &pm_io_base))
+		    return -ENODEV;
+
+	pm_io_base &= (0xff << 8);
+	return 0;
+}
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_via_init(void)
+{
+	if (find_via() < 0) {
+		printk("Error while reading PCI configuration\n");
+		return -ENODEV;
+	}
+
+	if (check_region(I2C_DIR, IOSPACE) < 0) {
+		printk("IO 0x%x-0x%x already in use\n",
+		       I2C_DIR, I2C_DIR + IOSPACE);
+		return -EBUSY;
+	} else {
+		request_region(I2C_DIR, IOSPACE, IOTEXT);
+		outb(inb(I2C_DIR) | I2C_SDA | I2C_SCL, I2C_DIR);
+		outb(inb(I2C_OUT) | I2C_SDA | I2C_SCL, I2C_OUT);
+	}
+
+	if (i2c_bit_add_bus(&bit_via_ops) == 0) {
+		printk("i2c-via.o: Module succesfully loaded\n");
+		return 0;
+	} else {
+		outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
+		release_region(I2C_DIR, IOSPACE);
+		printk
+		    ("i2c-via.o: Algo-bit error, couldn't register bus\n");
+		return -ENODEV;
+	}
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge");
+
+int init_module(void)
+{
+	return i2c_via_init();
+}
+
+void cleanup_module(void)
+{
+	i2c_bit_del_bus(&bit_via_ops);
+	release_region(I2C_DIR, IOSPACE);
+}
+#endif
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-viapro.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-viapro.c	(revision 949)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-viapro.c	(revision 949)
@@ -0,0 +1,556 @@
+/*
+    i2c-viapro.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>, 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.
+*/
+
+/* Note: we assume there can only be one VT596A/B, with one SMBus interface */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "version.h"
+
+#include <linux/init.h>
+
+#ifndef PCI_DEVICE_ID_VIA_82C596_3
+#define PCI_DEVICE_ID_VIA_82C596_3 0x3050
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C596B_3
+#define PCI_DEVICE_ID_VIA_82C596B_3	0x3051
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C686_4
+#define PCI_DEVICE_ID_VIA_82C686_4 0x3057
+#endif
+
+/* SMBus address offsets */
+#define SMBHSTSTS (0 + vt596_smba)
+#define SMBHSLVSTS (1 + vt596_smba)
+#define SMBHSTCNT (2 + vt596_smba)
+#define SMBHSTCMD (3 + vt596_smba)
+#define SMBHSTADD (4 + vt596_smba)
+#define SMBHSTDAT0 (5 + vt596_smba)
+#define SMBHSTDAT1 (6 + vt596_smba)
+#define SMBBLKDAT (7 + vt596_smba)
+#define SMBSLVCNT (8 + vt596_smba)
+#define SMBSHDWCMD (9 + vt596_smba)
+#define SMBSLVEVT (0xA + vt596_smba)
+#define SMBSLVDAT (0xC + vt596_smba)
+
+/* PCI Address Constants */
+
+/* SMBus data in configuration space can be found in two places,
+   We try to select the better one*/
+
+static unsigned short smb_cf_base;
+static unsigned short smb_cf_hstcfg;
+
+#define SMBBA1	    0x90
+#define SMBBA2      0x80
+#define SMBBA       (smb_cf_base)
+#define SMBHSTCFG   (smb_cf_hstcfg)
+#define SMBSLVC     (SMBHSTCFG+1)
+#define SMBSHDW1    (SMBHSTCFG+2)
+#define SMBSHDW2    (SMBHSTCFG+3)
+#define SMBREV      (SMBHSTCFG+4)
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+#define  ENABLE_INT9 0
+
+/* 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
+
+/* insmod parameters */
+
+/* If force is set to anything different from 0, we forcibly enable the
+   VT596. DANGEROUS! */
+static int force = 0;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the VT82C596 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 = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the VT82C596 SMBus at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_vt596_init(void);
+static int __init vt596_cleanup(void);
+static int vt596_setup(void);
+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);
+static void vt596_do_pause(unsigned int amount);
+static int vt596_transaction(void);
+static void vt596_inc(struct i2c_adapter *adapter);
+static void vt596_dec(struct i2c_adapter *adapter);
+static u32 vt596_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_access */ vt596_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ vt596_func,
+};
+
+static struct i2c_adapter vt596_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2,
+	&smbus_algorithm,
+	NULL,
+	vt596_inc,
+	vt596_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata vt596_initialized;
+static unsigned short vt596_smba = 0;
+
+
+/* Detect whether a VT596 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 vt596_setup(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	struct pci_dev *VT596_dev;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-viapro.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the VT596 function 3   _or_  VT686 function 4 */
+	VT596_dev = NULL;
+	VT596_dev = pci_find_device(PCI_VENDOR_ID_VIA,
+				    PCI_DEVICE_ID_VIA_82C596_3, VT596_dev);
+	if (VT596_dev == NULL)
+		VT596_dev = pci_find_device(PCI_VENDOR_ID_VIA,
+					    PCI_DEVICE_ID_VIA_82C686_4,
+					    VT596_dev);
+
+	if (VT596_dev == NULL)
+		VT596_dev = pci_find_device(PCI_VENDOR_ID_VIA,
+					    PCI_DEVICE_ID_VIA_82C596B_3,
+					    VT596_dev);
+
+	if (VT596_dev == NULL) {
+		printk
+		    ("i2c-viapro.o: Error: Can't detect vt82c596 or vt82c686");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Determine the configuration space registers for the SMBus areas */
+	if ((!pci_read_config_word(VT596_dev, SMBBA1, &vt596_smba))
+	    && (vt596_smba & 0x1)) {
+		smb_cf_base = SMBBA1;
+		smb_cf_hstcfg = 0xD2;
+	} else if ((!pci_read_config_word(VT596_dev, SMBBA2, &vt596_smba))
+		   && (vt596_smba & 0x1)) {
+		smb_cf_base = SMBBA2;
+		smb_cf_hstcfg = 0x84;
+	} else {
+		printk
+		    ("i2c-viapro.o: Cannot configure SMBus I/O Base address\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		vt596_smba = force_addr & 0xfff0;
+		force = 0;
+	} else {
+		vt596_smba &= 0xfff0;
+	}
+
+	if (check_region(vt596_smba, 8)) {
+		printk
+		    ("i2c-viapro.o: vt82c596_smb region 0x%x already in use!\n",
+		     vt596_smba);
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	pci_read_config_byte(VT596_dev, 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(VT596_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(VT596_dev, SMBBA, vt596_smba);
+		pci_write_config_byte(VT596_dev, SMBHSTCFG, temp | 0x01);
+		printk
+		    ("i2c-viapro.o: WARNING: VT596 SMBus interface set to new "
+		     "address %04x!\n", vt596_smba);
+	} else if ((temp & 1) == 0) {
+		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(VT596_dev, SMBHSTCFG,
+					      temp | 1);
+			printk
+			    ("i2c-viapro.o: WARNING: SMBus interface has been FORCEFULLY "
+			     "ENABLED!\n");
+		} else {
+			printk
+			    ("SMBUS: Error: Host SMBus controller not enabled!\n");
+			error_return = -ENODEV;
+			goto END;
+		}
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(vt596_smba, 8, "via2-smbus");
+
+#ifdef DEBUG
+	if ((temp & 0x0E) == 8)
+		printk("i2c-viapro.o: using Interrupt 9 for SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		printk("i2c-viapro.o: using Interrupt SMI# for SMBus.\n");
+	else
+		printk
+		    ("i2c-viapro.o: Illegal Interrupt configuration (or code out "
+		     "of date)!\n");
+
+	pci_read_config_byte(VT596_dev, SMBREV, &temp);
+	printk("i2c-viapro.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-viapro.o: VT596_smba = 0x%X\n", vt596_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+/* Internally used pause function */
+void vt596_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int vt596_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-viapro.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("i2c-viapro.o: SMBus busy (%02x). Resetting... \n",
+		       temp);
+#endif
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+			printk("i2c-viapro.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("i2c-viapro.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! 
+	   I don't know if VIA needs this, Intel did  */
+	do {
+		vt596_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+#ifdef DEBUG
+		printk("i2c-viapro.o: SMBus Timeout!\n");
+		result = -1;
+#endif
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-viapro.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		printk
+		    ("i2c-viapro.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("i2c-viapro.o: Error: no response!\n");
+#endif
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+		printk
+		    ("i2c-viapro.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+#endif
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-viapro.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. See smbus.h for more information */
+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, len;
+
+	switch (size) {
+	case I2C_SMBUS_PROC_CALL:
+		printk
+		    ("i2c-viapro.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		return -1;
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = VT596_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 = VT596_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 = VT596_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 = VT596_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 = VT596_BLOCK_DATA;
+		break;
+	}
+
+	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (vt596_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case VT596_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 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_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;
+}
+
+void vt596_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void vt596_dec(struct i2c_adapter *adapter)
+{
+
+	MOD_DEC_USE_COUNT;
+}
+
+u32 vt596_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;
+}
+
+int __init i2c_vt596_init(void)
+{
+	int res;
+	printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (vt596_initialized) {
+		printk
+		    ("i2c-viapro.o: Oops, vt596_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	vt596_initialized = 0;
+	if ((res = vt596_setup())) {
+		printk
+		    ("i2c-viapro.o: vt82c596 not detected, module not inserted.\n");
+		vt596_cleanup();
+		return res;
+	}
+	vt596_initialized++;
+	sprintf(vt596_adapter.name, "SMBus vt82c596 adapter at %04x",
+		vt596_smba);
+	if ((res = i2c_add_adapter(&vt596_adapter))) {
+		printk
+		    ("i2c-viapro.o: Adapter registration failed, module not inserted.\n");
+		vt596_cleanup();
+		return res;
+	}
+	vt596_initialized++;
+	printk("i2c-viapro.o: vt82c596 bus detected and initialized\n");
+	return 0;
+}
+
+int __init vt596_cleanup(void)
+{
+	int res;
+	if (vt596_initialized >= 2) {
+		if ((res = i2c_del_adapter(&vt596_adapter))) {
+			printk
+			    ("i2c-viapro.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			vt596_initialized--;
+	}
+	if (vt596_initialized >= 1) {
+		release_region(vt596_smba, 8);
+		vt596_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("vt82c596 SMBus driver");
+
+
+int init_module(void)
+{
+	return i2c_vt596_init();
+}
+
+int cleanup_module(void)
+{
+	return vt596_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i801.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i801.c	(revision 990)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i801.c	(revision 990)
@@ -0,0 +1,701 @@
+/*
+    i801.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 D. Studebaker
+    <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This driver supports the Intel 82801AA, 82801AB, and 82801BA
+    I/O Controller Hubs (ICH). 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.
+*/
+
+/* Note: we assume there can only be one I801, with one SMBus interface */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.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 "version.h"
+
+#ifndef PCI_DEVICE_ID_INTEL_82801AA_3
+#define PCI_DEVICE_ID_INTEL_82801AA_3   0x2413
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801AB_3
+#define PCI_DEVICE_ID_INTEL_82801AB_3   0x2423
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA_3
+#define PCI_DEVICE_ID_INTEL_82801BA_3   0x2443
+#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)
+
+/* 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 500
+#define  ENABLE_INT9 0
+
+/* I801 command constants */
+#define I801_QUICK          0x00
+#define I801_BYTE           0x04
+#define I801_BYTE_DATA      0x08
+#define I801_WORD_DATA      0x0C
+#define I801_BLOCK_DATA     0x14
+#define I801_I2C_BLOCK_DATA 0x18	/* unimplemented */
+#define I801_BLOCK_LAST     0x34
+#define I801_I2C_BLOCK_LAST 0x38	/* unimplemented */
+
+/* insmod parameters */
+
+/* If force is set to anything different from 0, we forcibly enable the
+   I801. DANGEROUS! */
+static int force = 0;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the I801. DANGEROUS!");
+
+/* 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!");
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_i801_init(void);
+static int __init i801_cleanup(void);
+static int i801_setup(void);
+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);
+static void i801_do_pause(unsigned int amount);
+static int i801_transaction(void);
+static int i801_block_transaction(union i2c_smbus_data *data,
+				  char read_write, int i2c_enable);
+static void i801_inc(struct i2c_adapter *adapter);
+static void i801_dec(struct i2c_adapter *adapter);
+static u32 i801_func(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static struct i2c_algorithm smbus_algorithm = {
+	/* name */ "Non-I2C SMBus adapter",
+	/* id */ I2C_ALGO_SMBUS,
+	/* master_xfer */ NULL,
+	/* smbus_xfer */ i801_access,
+	/* slave_send */ NULL,
+	/* slave_rcv */ NULL,
+	/* algo_control */ NULL,
+	/* functionality */ i801_func,
+};
+
+static struct i2c_adapter i801_adapter = {
+	"unset",
+	I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801,
+	&smbus_algorithm,
+	NULL,
+	i801_inc,
+	i801_dec,
+	NULL,
+	NULL,
+};
+
+static int __initdata i801_initialized;
+static unsigned short i801_smba = 0;
+static struct pci_dev *I801_dev = NULL;
+
+
+/* Detect whether a I801 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 i801_setup(void)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	/* First check whether we can access PCI at all */
+	if (pci_present() == 0) {
+		printk("i2c-i801.o: Error: No PCI-bus found!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	/* Look for the I801, function 3 */
+	/* Have to check for both the 82801AA and 82801AB */
+	/* Note: we keep on searching until we have found 'function 3' */
+	I801_dev = NULL;
+	do
+		I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					   PCI_DEVICE_ID_INTEL_82801AA_3,
+					   I801_dev);
+	while (I801_dev && (PCI_FUNC(I801_dev->devfn) != 3));
+	if (I801_dev == NULL) {
+		do
+			I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					   PCI_DEVICE_ID_INTEL_82801AB_3,
+					   I801_dev);
+		while (I801_dev && (PCI_FUNC(I801_dev->devfn) != 3));
+	}
+	if (I801_dev == NULL) {
+		do
+			I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					   PCI_DEVICE_ID_INTEL_82801BA_3,
+					   I801_dev);
+		while (I801_dev && (PCI_FUNC(I801_dev->devfn) != 3));
+	}
+	if (I801_dev == NULL) {
+		printk
+		    ("i2c-i801.o: Error: Can't detect I801, function 3!\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		i801_smba = force_addr & 0xfff0;
+		force = 0;
+	} else {
+		pci_read_config_word(I801_dev, SMBBA, &i801_smba);
+		i801_smba &= 0xfff0;
+	}
+
+	if (check_region(i801_smba, 8)) {
+		printk
+		    ("i2c-i801.o: I801_smb region 0x%x already in use!\n",
+		     i801_smba);
+		error_return = -ENODEV;
+		goto END;
+	}
+
+	pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
+/* If force_addr is set, we program the new address here. Just to make
+   sure, we disable the I801 first. */
+	if (force_addr) {
+		pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(I801_dev, SMBBA, i801_smba);
+		pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01);
+		printk
+		    ("i2c-i801.o: WARNING: I801 SMBus interface set to new "
+		     "address %04x!\n", i801_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(I801_dev, SMBHSTCFG,
+					      temp | 1);
+			printk
+			    ("i2c-i801.o: WARNING: I801 SMBus interface has been FORCEFULLY "
+			     "ENABLED!\n");
+		} else {
+			printk
+			    ("SMBUS: Error: Host SMBus controller not enabled!\n");
+			error_return = -ENODEV;
+			goto END;
+		}
+	}
+
+	/* note: we assumed that the BIOS picked SMBus or I2C Bus timing
+	   appropriately (bit 2 in SMBHSTCFG) */
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(i801_smba, 8, "i801-smbus");
+
+#ifdef DEBUG
+	if (temp & 0x02)
+		printk
+		    ("i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n");
+	else
+		printk
+		    ("i2c-i801.o: I801 using PCI Interrupt for SMBus.\n");
+
+	pci_read_config_byte(I801_dev, SMBREV, &temp);
+	printk("i2c-i801.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-i801.o: I801_smba = 0x%X\n", i801_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+/* Internally used pause function */
+void i801_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int i801_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-i801.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 */
+	/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+#ifdef DEBUG
+		printk("i2c-i801.o: SMBus busy (%02x). Resetting... \n",
+		       temp);
+#endif
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+			printk("i2c-i801.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("i2c-i801.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! */
+	do {
+		i801_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+#ifdef DEBUG
+		printk("i2c-i801.o: SMBus Timeout!\n");
+		result = -1;
+#endif
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-i801.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		printk
+		    ("i2c-i801.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("i2c-i801.o: Error: no response!\n");
+#endif
+	}
+
+	if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+#ifdef DEBUG
+		printk
+		    ("i2c-i801.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+#endif
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-i801.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;
+}
+
+/* All-inclusive block transaction function */
+int i801_block_transaction(union i2c_smbus_data *data, char read_write, 
+                           int i2c_enable)
+{
+	int i, len;
+	int smbcmd;
+	int temp;
+	int result = 0;
+	int timeout = 0;
+        unsigned char hostc, errmask;
+
+        if (i2c_enable) {
+                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 {
+                        printk("i2c-i801.o: "
+                               "I2C_SMBUS_I2C_BLOCK_READ not supported!\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 */
+	}
+
+	for (i = 1; i <= len; i++) {
+		if (i == len && read_write == I2C_SMBUS_READ)
+			smbcmd = I801_BLOCK_LAST;
+		else
+			smbcmd = I801_BLOCK_DATA;
+
+		outb_p((smbcmd & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+#ifdef DEBUG
+		printk
+		    ("i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, "
+		     "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT),
+		     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		     inb_p(SMBBLKDAT));
+#endif
+
+		/* 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) {
+#ifdef DEBUG
+			printk
+			    ("i2c-i801.o: SMBus busy (%02x). Resetting... \n",
+			     temp);
+#endif
+			outb_p(temp, SMBHSTSTS);
+			if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+				printk
+				    ("i2c-i801.o: Reset failed! (%02x)\n",
+				     temp);
+				result = -1;
+                                goto END;
+			}
+			if (i != 1) {
+                                result = -1;  /* if die in middle of block transaction, fail */
+                                goto END;
+                        }
+		}
+
+		/* start the transaction by setting bit 6 */
+		if (i==1) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
+
+		/* We will always wait for a fraction of a second! */
+		do {
+			temp = inb_p(SMBHSTSTS);
+			i801_do_pause(1);
+		}
+		    while (
+			   (((i >= len) && (temp & 0x01))
+			    || ((i < len) && !(temp & 0x80)))
+			   && (timeout++ < MAX_TIMEOUT));
+
+		/* If the SMBus is still busy, we give up */
+		if (timeout >= MAX_TIMEOUT) {
+			result = -1;
+#ifdef DEBUG
+			printk("i2c-i801.o: SMBus Timeout!\n");
+#endif
+		}
+
+		if (temp & 0x10) {
+			result = -1;
+#ifdef DEBUG
+			printk
+			    ("i2c-i801.o: Error: Failed bus transaction\n");
+#endif
+		} else if (temp & 0x08) {
+			result = -1;
+			printk
+			    ("i2c-i801.o: Bus collision! SMBus may be locked until next hard"
+			     " reset. (sorry!)\n");
+			/* Clock stops and slave is stuck in mid-transmission */
+		} else if (temp & 0x04) {
+			result = -1;
+#ifdef DEBUG
+			printk("i2c-i801.o: Error: no response!\n");
+#endif
+		}
+
+		if (i == 1 && read_write == I2C_SMBUS_READ) {
+			len = inb_p(SMBHSTDAT0);
+			if (len < 1)
+				len = 1;
+			if (len > 32)
+				len = 32;
+			data->block[0] = len;
+		}
+
+                /* 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 */
+
+		temp = inb_p(SMBHSTSTS);
+		if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+#ifdef DEBUG
+			printk
+			    ("i2c-i801.o: Failed reset at end of transaction (%02x)\n",
+			     temp);
+#endif
+		}
+#ifdef DEBUG
+		printk
+		    ("i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
+		     "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT),
+		     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		     inb_p(SMBBLKDAT));
+#endif
+
+		if (result < 0) {
+			goto END;
+                }
+	}
+        result = 0;
+END:
+        if (i2c_enable) {
+                /* restore saved configuration register value */
+		pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
+        }
+	return result;
+}
+
+/* Return -1 on error. See smbus.h for more information */
+s32 i801_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_PROC_CALL:
+		printk("i2c-i801.o: I2C_SMBUS_PROC_CALL not supported!\n");
+		return -1;
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = 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);
+		size = 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);
+		size = 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);
+		}
+		size = 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 transactions are very different from piix4 block
+		   and from the other i801 transactions. Handle in the
+		   i801_block_transaction() routine. */
+		return i801_block_transaction(data, read_write, 
+                                              size==I2C_SMBUS_I2C_BLOCK_DATA);
+	}
+
+	/* 'size' is really the transaction type */
+	outb_p((size & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (i801_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == I801_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case I801_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	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;
+}
+
+void i801_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void i801_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+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;
+}
+
+int __init i2c_i801_init(void)
+{
+	int res;
+	printk("i2c-i801.o version %s (%s)\n", LM_VERSION, LM_DATE);
+#ifdef DEBUG
+/* PE- It might be good to make this a permanent part of the code! */
+	if (i801_initialized) {
+		printk
+		    ("i2c-i801.o: Oops, i801_init called a second time!\n");
+		return -EBUSY;
+	}
+#endif
+	i801_initialized = 0;
+	if ((res = i801_setup())) {
+		printk
+		    ("i2c-i801.o: I801 not detected, module not inserted.\n");
+		i801_cleanup();
+		return res;
+	}
+	i801_initialized++;
+	sprintf(i801_adapter.name, "SMBus I801 adapter at %04x",
+		i801_smba);
+	if ((res = i2c_add_adapter(&i801_adapter))) {
+		printk
+		    ("i2c-i801.o: Adapter registration failed, module not inserted.\n");
+		i801_cleanup();
+		return res;
+	}
+	i801_initialized++;
+	printk("i2c-i801.o: I801 bus detected and initialized\n");
+	return 0;
+}
+
+int __init i801_cleanup(void)
+{
+	int res;
+	if (i801_initialized >= 2) {
+		if ((res = i2c_del_adapter(&i801_adapter))) {
+			printk
+			    ("i2c-i801.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		} else
+			i801_initialized--;
+	}
+	if (i801_initialized >= 1) {
+		release_region(i801_smba, 8);
+		i801_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("I801 SMBus driver");
+
+int init_module(void)
+{
+	return i2c_i801_init();
+}
+
+int cleanup_module(void)
+{
+	return i801_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i810.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i810.c	(revision 941)
+++ /lm-sensors/tags/V2-5-5/kernel/busses/i2c-i810.c	(revision 941)
@@ -0,0 +1,344 @@
+/*
+    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           
+   i815			1132           
+*/
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "version.h"
+#include <linux/init.h>
+
+/* PCI defines */
+#ifndef PCI_DEVICE_ID_INTEL_82810_IG1
+#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82810_IG3
+#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82815_2
+#define PCI_DEVICE_ID_INTEL_82815_2   0x1132
+#endif
+
+static int i810_supported[] = {PCI_DEVICE_ID_INTEL_82810_IG1,
+                               PCI_DEVICE_ID_INTEL_82810_IG3,
+                               PCI_DEVICE_ID_INTEL_82815_2,
+                               0 };
+
+/* 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			50
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init i2c_i810i2c_init(void);
+static int __init i810i2c_cleanup(void);
+static int i810i2c_setup(void);
+static void config_i810(struct pci_dev *dev);
+static void i810_inc(struct i2c_adapter *adapter);
+static void i810_dec(struct i2c_adapter *adapter);
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+static int __initdata i810i2c_initialized;
+static unsigned char *mem;
+
+static inline void outlong(unsigned int dat, int off)
+{
+	*((unsigned int *) (mem + off)) = dat;
+}
+
+static inline unsigned int readlong(int off)
+{
+	return *((unsigned int *) (mem + off));
+}
+
+/* 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)
+{
+	outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     I810_GPIOB);
+}
+
+static void bit_i810i2c_setsda(void *data, int val)
+{
+ 	outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     I810_GPIOB);
+}
+
+/* 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. 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. This is necessary to get
+   i2c_algo_bit bit_test=1 to pass. */
+
+static int bit_i810i2c_getscl(void *data)
+{
+	outlong(0, I810_GPIOB);
+	return (0 != (readlong(I810_GPIOB) & SCL_VAL_IN));
+}
+
+static int bit_i810i2c_getsda(void *data)
+{
+	outlong(0, I810_GPIOB);
+	return (0 != (readlong(I810_GPIOB) & SDA_VAL_IN));
+}
+
+static void bit_i810ddc_setscl(void *data, int val)
+{
+	outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     I810_GPIOA);
+}
+
+static void bit_i810ddc_setsda(void *data, int val)
+{
+ 	outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     I810_GPIOA);
+}
+
+static int bit_i810ddc_getscl(void *data)
+{
+	outlong(0, I810_GPIOA);
+	return (0 != (readlong(I810_GPIOA) & SCL_VAL_IN));
+}
+
+static int bit_i810ddc_getsda(void *data)
+{
+	outlong(0, I810_GPIOA);
+	return (0 != (readlong(I810_GPIOA) & SDA_VAL_IN));
+}
+
+static struct i2c_algo_bit_data i810_i2c_bit_data = {
+	NULL,
+	bit_i810i2c_setsda,
+	bit_i810i2c_setscl,
+	bit_i810i2c_getsda,
+	bit_i810i2c_getscl,
+	CYCLE_DELAY, CYCLE_DELAY, TIMEOUT
+};
+
+static struct i2c_adapter i810_i2c_adapter = {
+	"I810/I815 I2C Adapter",
+	I2C_HW_B_I810,
+	NULL,
+	&i810_i2c_bit_data,
+	i810_inc,
+	i810_dec,
+	NULL,
+	NULL,
+};
+
+static struct i2c_algo_bit_data i810_ddc_bit_data = {
+	NULL,
+	bit_i810ddc_setsda,
+	bit_i810ddc_setscl,
+	bit_i810ddc_getsda,
+	bit_i810ddc_getscl,
+	CYCLE_DELAY, CYCLE_DELAY, TIMEOUT
+};
+
+static struct i2c_adapter i810_ddc_adapter = {
+	"I810/I815 DDC Adapter",
+	I2C_HW_B_I810,
+	NULL,
+	&i810_ddc_bit_data,
+	i810_inc,
+	i810_dec,
+	NULL,
+	NULL,
+};
+
+
+/* Configures the chip */
+void config_i810(struct pci_dev *dev)
+{
+	unsigned long cadr;
+
+	/* map I810 memory */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
+	cadr = dev->resource[1].start;
+#else
+	cadr = dev->base_address[1];
+#endif
+	cadr += I810_IOCONTROL_OFFSET;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	mem = ioremap_nocache(cadr, 0x1000);
+	bit_i810i2c_setscl(NULL, 1);
+	bit_i810i2c_setsda(NULL, 1);
+	bit_i810ddc_setscl(NULL, 1);
+	bit_i810ddc_setsda(NULL, 1);
+}
+
+/* Detect whether a supported device can be found,
+   and initialize it */
+static int i810i2c_setup(void)
+{
+	struct pci_dev *dev = NULL;
+	int *num = i810_supported;
+
+	do {
+		if ((dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					   *num++, dev))) {
+			config_i810(dev);
+			printk(KERN_INFO
+			       "i2c-i810.o: i810/i815 found.\n");
+			return 0;
+		}
+	} while (*num != 0);
+
+	return -ENODEV;
+}
+
+
+void i810_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void i810_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+int __init i2c_i810i2c_init(void)
+{
+	int res;
+	printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	i810i2c_initialized = 0;
+	if ((res = i810i2c_setup())) {
+		printk
+		    ("i2c-i810.o: i810/i815 not detected, module not inserted.\n");
+		i810i2c_cleanup();
+		return res;
+	}
+	if ((res = i2c_bit_add_bus(&i810_i2c_adapter))) {
+		printk("i2c-i810.o: I2C adapter registration failed\n");
+	} else {
+		printk("i2c-i810.o: I810/I815 I2C bus initialized\n");
+		i810i2c_initialized |= INIT2;
+	}
+	if ((res = i2c_bit_add_bus(&i810_ddc_adapter))) {
+		printk("i2c-i810.o: DDC adapter registration failed\n");
+	} else {
+		printk("i2c-i810.o: I810/I815 DDC bus initialized\n");
+		i810i2c_initialized |= INIT3;
+	}
+	if(!(i810i2c_initialized & (INIT2 | INIT3))) {
+		printk("i2c-i810.o: Both registrations failed, module not inserted\n");
+		i810i2c_cleanup();
+		return res;
+	}
+	return 0;
+}
+
+int __init i810i2c_cleanup(void)
+{
+	int res;
+
+	iounmap(mem);
+	if (i810i2c_initialized & INIT3) {
+		if ((res = i2c_bit_del_bus(&i810_ddc_adapter))) {
+			printk
+			    ("i2c-i810.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		}
+	}
+	if (i810i2c_initialized & INIT2) {
+		if ((res = i2c_bit_del_bus(&i810_i2c_adapter))) {
+			printk
+			    ("i2c-i810.o: i2c_del_adapter failed, module not removed\n");
+			return res;
+		}
+	}
+	i810i2c_initialized = 0;
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+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");
+
+
+int init_module(void)
+{
+	return i2c_i810i2c_init();
+}
+
+int cleanup_module(void)
+{
+	return i810i2c_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/maxilife.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/maxilife.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/maxilife.c	(revision 933)
@@ -0,0 +1,1437 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "sensors.h"
+#include "i2c-isa.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+
+#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)))
+
+extern 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)
+
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* 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 semaphore lock;
+	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 */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_maxi_init(void);
+static int __init maxi_cleanup(void);
+
+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_command(struct i2c_client *client, unsigned int cmd,
+			void *arg);
+static void maxi_inc_use(struct i2c_client *client);
+static void maxi_dec_use(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);
+static int maxi_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+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,
+	/* command */ &maxi_command,
+	/* inc_use */ &maxi_inc_use,
+	/* dec_use */ &maxi_dec_use
+};
+
+/* Used by maxi_init/cleanup */
+static int __initdata maxi_initialized = 0;
+
+static int maxi_id = 0;
+
+/* 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 */
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_pll},
+	{MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_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) */
+int maxi_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, maxi_detect);
+}
+
+/* This function is called by sensors_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;
+	int i, j, 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 maxi_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct maxi_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	/* Fill the new client structure with data */
+	data = (struct maxi_data *) (new_client + 1);
+	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;
+
+	new_client->id = maxi_id++;
+
+	data->valid = 0;
+	init_MUTEX(&data->lock);
+	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 = sensors_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(new_client);
+      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). */
+int maxi_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+	return 0;
+}
+
+/* No commands defined yet */
+int maxi_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void maxi_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void maxi_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+/* Read byte from specified register (-1 in case of error, value otherwise). */
+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 */
+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;
+}
+
+/* Write byte to specified register (-1 in case of error, 0 otherwise). */
+int maxi_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* 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. It should set limits, etc. */
+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]);
+	}
+}
+
+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 last_updated[6][6];	/* sensor, which */
+	struct maxi_data *data = client->data;
+	int i;
+
+	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;
+	}
+}
+
+int __init sensors_maxi_init(void)
+{
+	int res;
+
+	printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str,
+	       LM_VERSION, LM_DATE);
+	maxi_initialized = 0;
+
+	if ((res = i2c_add_driver(&maxi_driver))) {
+		printk
+		    ("maxilife: Driver registration failed, module not inserted.\n");
+		maxi_cleanup();
+		return res;
+	}
+	maxi_initialized++;
+	return 0;
+}
+
+int __init maxi_cleanup(void)
+{
+	int res;
+
+	if (maxi_initialized >= 1) {
+		if ((res = i2c_del_driver(&maxi_driver))) {
+			printk
+			    ("maxilife: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		maxi_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+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");
+
+int init_module(void)
+{
+	return sensors_maxi_init();
+}
+
+int cleanup_module(void)
+{
+	return maxi_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/thmc50.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/thmc50.c	(revision 960)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/thmc50.c	(revision 960)
@@ -0,0 +1,582 @@
+/*
+    thmc50.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.
+*/
+
+#define DEBUG 1
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2D, 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(thmc50);
+
+/* Many THMC50 constants specified below */
+
+/* The THMC50 registers */
+#define THMC50_REG_TEMP 0x27
+#define THMC50_REG_CONF 0x40
+#define THMC50_REG_TEMP_HYST 0x3A
+#define THMC50_REG_TEMP_OS 0x39
+
+#define THMC50_REG_TEMP_TRIP 0x13
+#define THMC50_REG_TEMP_REMOTE_TRIP 0x14
+#define THMC50_REG_TEMP_DEFAULT_TRIP 0x17
+#define THMC50_REG_TEMP_REMOTE_DEFAULT_TRIP 0x18
+#define THMC50_REG_ANALOG_OUT 0x19
+#define THMC50_REG_REMOTE_TEMP 0x26
+#define THMC50_REG_REMOTE_TEMP_HYST 0x38
+#define THMC50_REG_REMOTE_TEMP_OS 0x37
+
+#define THMC50_REG_INTER 0x41
+#define THMC50_REG_INTER_MIRROR 0x4C
+#define THMC50_REG_INTER_MASK 0x43
+
+#define THMC50_REG_COMPANY_ID 0x3E
+#define THMC50_REG_DIE_CODE 0x3F
+
+
+/* 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>127)?val - 0x0100:val)
+#define TEMP_TO_REG(val)   ((val<0)?0x0100+val:val)
+
+/* Initial values */
+#define THMC50_INIT_TEMP_OS 60
+#define THMC50_INIT_TEMP_HYST 50
+
+/* Each client has this additional data */
+struct thmc50_data {
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_os, temp_hyst,
+	    remote_temp, remote_temp_os, remote_temp_hyst,
+	    inter, inter_mask, die_code, analog_out;	/* Register values */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_thmc50_init(void);
+static int __init thmc50_cleanup(void);
+static int thmc50_attach_adapter(struct i2c_adapter *adapter);
+static int thmc50_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void thmc50_init_client(struct i2c_client *client);
+static int thmc50_detach_client(struct i2c_client *client);
+static int thmc50_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+static void thmc50_inc_use(struct i2c_client *client);
+static void thmc50_dec_use(struct i2c_client *client);
+static u16 swap_bytes(u16 val);
+static int thmc50_read_value(struct i2c_client *client, u8 reg);
+static int thmc50_write_value(struct i2c_client *client, u8 reg,
+			      u16 value);
+static void thmc50_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void thmc50_remote_temp(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void thmc50_inter(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void thmc50_inter_mask(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void thmc50_die_code(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void thmc50_analog_out(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void thmc50_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver thmc50_driver = {
+	/* name */ "THMC50 sensor chip driver",
+	/* id */ I2C_DRIVERID_THMC50,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &thmc50_attach_adapter,
+	/* detach_client */ &thmc50_detach_client,
+	/* command */ &thmc50_command,
+	/* inc_use */ &thmc50_inc_use,
+	/* dec_use */ &thmc50_dec_use
+};
+
+/* These files are created for each detected THMC50. 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 thmc50_dir_table_template[] = {
+	{THMC50_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_temp},
+	{THMC50_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_remote_temp},
+	{THMC50_SYSCTL_INTER, "inter", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_inter},
+	{THMC50_SYSCTL_INTER_MASK, "inter_mask", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_inter_mask},
+	{THMC50_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_die_code},
+	{THMC50_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &thmc50_analog_out},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata thmc50_initialized = 0;
+
+static int thmc50_id = 0;
+
+int thmc50_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, thmc50_detect);
+}
+
+/* This function is called by sensors_detect */
+int thmc50_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int company, i;
+	struct i2c_client *new_client;
+	struct thmc50_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+#ifdef DEBUG
+	printk("thmc50.o: Probing for THMC50 at 0x%2X on bus %d\n",
+	       address, adapter->id);
+#endif
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("thmc50.o: thmc50_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 thmc50_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct thmc50_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data =
+	    (struct thmc50_data *) (((struct i2c_client *) new_client) +
+				    1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &thmc50_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+	company =
+	    i2c_smbus_read_byte_data(new_client, THMC50_REG_COMPANY_ID);
+
+	if (company != 0x49) {
+#ifdef DEBUG
+		printk
+		    ("thmc50.o: Detect of THMC50 failed (reg 3E: 0x%X)\n",
+		     company);
+#endif
+		goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	kind = thmc50;
+
+	if (kind == thmc50) {
+		type_name = "thmc50";
+		client_name = "THMC50 chip";
+	} else {
+#ifdef DEBUG
+		printk("thmc50.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);
+
+	new_client->id = thmc50_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client, type_name,
+					thmc50_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	thmc50_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int thmc50_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct thmc50_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("thmc50.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int thmc50_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void thmc50_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void thmc50_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* All registers are word-sized, except for the configuration register.
+   THMC50 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int thmc50_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* All registers are word-sized, except for the configuration register.
+   THMC50 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int thmc50_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+void thmc50_init_client(struct i2c_client *client)
+{
+	/* Initialize the THMC50 chip */
+	thmc50_write_value(client, THMC50_REG_TEMP_OS,
+			   TEMP_TO_REG(THMC50_INIT_TEMP_OS));
+	thmc50_write_value(client, THMC50_REG_TEMP_HYST,
+			   TEMP_TO_REG(THMC50_INIT_TEMP_HYST));
+	thmc50_write_value(client, THMC50_REG_CONF, 1);
+}
+
+void thmc50_update_client(struct i2c_client *client)
+{
+	struct thmc50_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 thmc50 update\n");
+#endif
+
+		data->temp = thmc50_read_value(client, THMC50_REG_TEMP);
+		data->temp_os =
+		    thmc50_read_value(client, THMC50_REG_TEMP_OS);
+		data->temp_hyst =
+		    thmc50_read_value(client, THMC50_REG_TEMP_HYST);
+		data->remote_temp =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP);
+		data->remote_temp_os =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_OS);
+		data->remote_temp_hyst =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_HYST);
+		data->inter = thmc50_read_value(client, THMC50_REG_INTER);
+		data->inter_mask =
+		    thmc50_read_value(client, THMC50_REG_INTER_MASK);
+		data->die_code =
+		    thmc50_read_value(client, THMC50_REG_DIE_CODE);
+		data->analog_out =
+		    thmc50_read_value(client, THMC50_REG_ANALOG_OUT);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void thmc50_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_os);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = TEMP_TO_REG(results[0]);
+			thmc50_write_value(client, THMC50_REG_TEMP_OS,
+					   data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			thmc50_write_value(client, THMC50_REG_TEMP_HYST,
+					   data->temp_hyst);
+		}
+	}
+}
+
+
+void thmc50_remote_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_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]);
+			thmc50_write_value(client,
+					   THMC50_REG_REMOTE_TEMP_OS,
+					   data->remote_temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->remote_temp_hyst = TEMP_TO_REG(results[1]);
+			thmc50_write_value(client,
+					   THMC50_REG_REMOTE_TEMP_HYST,
+					   data->remote_temp_hyst);
+		}
+	}
+}
+
+
+void thmc50_inter(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->inter;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk("thmc50.o: No writes to Interrupt register!\n");
+	}
+}
+
+
+void thmc50_inter_mask(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->inter_mask;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->inter_mask = results[0];
+			thmc50_write_value(client, THMC50_REG_INTER_MASK,
+					   data->inter_mask);
+		}
+	}
+}
+
+
+void thmc50_die_code(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->die_code;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk("thmc50.o: No writes to Die-Code register!\n");
+	}
+}
+
+
+void thmc50_analog_out(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_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];
+			thmc50_write_value(client, THMC50_REG_ANALOG_OUT,
+					   data->analog_out);
+		}
+	}
+}
+
+
+
+
+int __init sensors_thmc50_init(void)
+{
+	int res;
+
+	printk("thmc50.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	thmc50_initialized = 0;
+	if ((res = i2c_add_driver(&thmc50_driver))) {
+		printk
+		    ("thmc50.o: Driver registration failed, module not inserted.\n");
+		thmc50_cleanup();
+		return res;
+	}
+	thmc50_initialized++;
+	return 0;
+}
+
+int __init thmc50_cleanup(void)
+{
+	int res;
+
+	if (thmc50_initialized >= 1) {
+		if ((res = i2c_del_driver(&thmc50_driver))) {
+			printk
+			    ("thmc50.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		thmc50_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("THMC50 driver");
+
+int init_module(void)
+{
+	return sensors_thmc50_init();
+}
+
+int cleanup_module(void)
+{
+	return thmc50_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/pcf8574.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/pcf8574.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/pcf8574.c	(revision 933)
@@ -0,0 +1,380 @@
+/*
+    pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>, 
+                        Philip Edelbrock <phil@netroedge.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.
+*/
+
+/* A few notes about the PCF8574:
+
+* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by
+  Philips Semiconductors.  It is designed to provide a byte I2C
+  interface to up to 8 separate devices.
+  
+* The PCF8574 appears as a very simple SMBus device which can be
+  read from or written to with SMBUS byte read/write accesses.
+
+* Because of the general purpose nature of this device, it will most
+  likely be necessary to customize the /proc interface to suit the
+  specific application.
+
+  --Dan
+
+*/
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x27, 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(pcf8574);
+
+/* The PCF8574 registers */
+
+/* (No registers.  [Wow! This thing is SIMPLE!] ) */
+
+/* Initial values */
+#define PCF8574_INIT 0		/* Both off */
+
+/* Each client has this additional data */
+struct pcf8574_data {
+	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 */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_pcf8574_init(void);
+static int __init pcf8574_cleanup(void);
+static int pcf8574_attach_adapter(struct i2c_adapter *adapter);
+static int pcf8574_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int pcf8574_detach_client(struct i2c_client *client);
+static int pcf8574_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void pcf8574_inc_use(struct i2c_client *client);
+static void pcf8574_dec_use(struct i2c_client *client);
+static void pcf8574_status(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void pcf8574_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pcf8574_driver = {
+	/* name */ "PCF8574 sensor chip driver",
+	/* id */ I2C_DRIVERID_PCF8574,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &pcf8574_attach_adapter,
+	/* detach_client */ &pcf8574_detach_client,
+	/* command */ &pcf8574_command,
+	/* inc_use */ &pcf8574_inc_use,
+	/* dec_use */ &pcf8574_dec_use
+};
+
+/* These files are created for each detected PCF8574. 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 pcf8574_dir_table_template[] = {
+	{PCF8574_SYSCTL_STAT, "status", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &pcf8574_status},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata pcf8574_initialized = 0;
+
+/* I choose here for semi-static PCF8574 allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int pcf8574_id = 0;
+
+int pcf8574_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, pcf8574_detect);
+}
+
+/* This function is called by sensors_detect */
+int pcf8574_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct pcf8574_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; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("pcf8574.o: pcf8574_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 pcf8574_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct pcf8574_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct pcf8574_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &pcf8574_driver;
+	new_client->flags = 0;
+
+	/* Now, we would do the remaining detection. But the PCF8574 is plainly
+	   impossible to detect! Stupid chip. */
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = pcf8574;
+
+	if (kind == pcf8574) {
+		type_name = "pcf8574";
+		client_name = "PCF8574 chip";
+	} else {
+#ifdef DEBUG
+		printk("pcf8574.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);
+
+	new_client->id = pcf8574_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client, type_name,
+					pcf8574_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(new_client);
+      ERROR0:
+	return err;
+}
+
+
+int pcf8574_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct pcf8574_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("pcf8574.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+
+}
+
+/* No commands defined yet */
+int pcf8574_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void pcf8574_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void pcf8574_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+void pcf8574_update_client(struct i2c_client *client)
+{
+	struct pcf8574_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 5*HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting pcf8574 update\n");
+#endif
+
+		data->status = i2c_smbus_read_byte(client); 
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void pcf8574_status(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	u8	tmpstatus; 
+
+	struct pcf8574_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		pcf8574_update_client(client);
+		tmpstatus = data->status;		
+		tmpstatus = (tmpstatus & 0xf0) >> 4;
+		results[0] = tmpstatus; 
+		tmpstatus = data->status;		
+		tmpstatus &= 0x0f;
+		results[1] = tmpstatus; 
+		*nrels_mag = 2;
+	} 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);
+		}
+	}
+}
+
+int __init sensors_pcf8574_init(void)
+{
+	int res;
+
+	printk("pcf8574.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	pcf8574_initialized = 0;
+	if ((res = i2c_add_driver(&pcf8574_driver))) {
+		printk
+		    ("pcf8574.o: Driver registration failed, module not inserted.\n");
+		pcf8574_cleanup();
+		return res;
+	}
+	pcf8574_initialized++;
+	return 0;
+}
+
+int __init pcf8574_cleanup(void)
+{
+	int res;
+
+	if (pcf8574_initialized >= 1) {
+		if ((res = i2c_del_driver(&pcf8574_driver))) {
+			printk
+			    ("pcf8574.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		pcf8574_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com and Dan Eaton <dan.eaton@rocketlogix.com>");
+MODULE_DESCRIPTION("PCF8574 driver");
+
+int init_module(void)
+{
+	return sensors_pcf8574_init();
+}
+
+int cleanup_module(void)
+{
+	return pcf8574_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/gl518sm.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/gl518sm.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/gl518sm.c	(revision 933)
@@ -0,0 +1,1123 @@
+/*
+    gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                              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/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+
+#include <linux/init.h>
+
+#ifdef __SMP__
+#include <linux/smp_lock.h>
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 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_2(gl518sm_r00, gl518sm_r80);
+
+/* Defining this will enable debug messages for the voltage iteration
+   code used with rev 0 ICs */
+#undef DEBUG_VIN
+
+/* Many GL518 constants specified below */
+
+/* The GL518 registers */
+#define GL518_REG_CHIP_ID 0x00
+#define GL518_REG_REVISION 0x01
+#define GL518_REG_VENDOR_ID 0x02
+#define GL518_REG_CONF 0x03
+#define GL518_REG_TEMP 0x04
+#define GL518_REG_TEMP_OVER 0x05
+#define GL518_REG_TEMP_HYST 0x06
+#define GL518_REG_FAN_COUNT 0x07
+#define GL518_REG_FAN_LIMIT 0x08
+#define GL518_REG_VIN1_LIMIT 0x09
+#define GL518_REG_VIN2_LIMIT 0x0a
+#define GL518_REG_VIN3_LIMIT 0x0b
+#define GL518_REG_VDD_LIMIT 0x0c
+#define GL518_REG_VIN3 0x0d
+#define GL518_REG_MISC 0x0f
+#define GL518_REG_ALARM 0x10
+#define GL518_REG_MASK 0x11
+#define GL518_REG_INT 0x12
+#define GL518_REG_VIN2 0x13
+#define GL518_REG_VIN1 0x14
+#define GL518_REG_VDD 0x15
+
+
+/* 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_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+119),\
+                                        0,255))
+#define TEMP_FROM_REG(val) (((val) - 119) * 10)
+
+extern inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) \
+ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) )
+
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255))
+#define IN_FROM_REG(val) (((val)*19)/10)
+
+#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255))
+#define VDD_FROM_REG(val) (((val)*23)/10)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+#define ALARMS_FROM_REG(val) val
+
+#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
+
+#define BEEPS_TO_REG(val) ((val) & 0x7f)
+#define BEEPS_FROM_REG(val) ((val) & 0x7f)
+
+/* Initial values */
+#define GL518_INIT_TEMP_OVER 600
+#define GL518_INIT_TEMP_HYST 500
+#define GL518_INIT_FAN_MIN_1 3000
+#define GL518_INIT_FAN_MIN_2 3000
+
+/* These are somewhat sane */
+#define GL518_INIT_VIN_1 330	/* 3.3 V */
+#define GL518_INIT_VIN_2 286	/* 12 V */
+#define GL518_INIT_VIN_3 260	/* Vcore */
+#define GL518_INIT_VDD 500	/* 5 V */
+
+#define GL518_INIT_PERCENTAGE 10
+
+#define GL518_INIT_VIN_MIN_1 \
+        (GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VIN_MAX_1 \
+        (GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VIN_MIN_2 \
+        (GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VIN_MAX_2 \
+        (GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VIN_MIN_3 \
+        (GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VIN_MAX_3 \
+        (GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VDD_MIN \
+        (GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
+#define GL518_INIT_VDD_MAX \
+        (GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
+
+
+/* Each client has this additional data */
+struct gl518_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+
+	int iterate_lock;
+	int quit_thread;
+	struct task_struct *thread;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+	wait_queue_head_t wq;
+#else
+	struct wait_queue *wq;
+#endif
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	unsigned long last_updated_v00;
+	/* In jiffies (used only by rev00 chips) */
+
+	u8 voltage[4];		/* Register values; [0] = VDD */
+	u8 voltage_min[4];	/* Register values; [0] = VDD */
+	u8 voltage_max[4];	/* Register values; [0] = VDD */
+	u8 iter_voltage[4];	/* Register values; [0] = VDD */
+	u8 fan[2];
+	u8 fan_min[2];
+	u8 temp;		/* Register values */
+	u8 temp_over;		/* Register values */
+	u8 temp_hyst;		/* Register values */
+	u8 alarms, beeps;	/* Register value */
+	u8 alarm_mask;		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u8 beep_enable;		/* Boolean */
+	u8 iterate;		/* Voltage iteration mode */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_gl518sm_init(void);
+static int __init gl518_cleanup(void);
+static int gl518_attach_adapter(struct i2c_adapter *adapter);
+static int gl518_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void gl518_init_client(struct i2c_client *client);
+static int gl518_detach_client(struct i2c_client *client);
+static int gl518_command(struct i2c_client *client, unsigned int cmd,
+			 void *arg);
+static void gl518_inc_use(struct i2c_client *client);
+static void gl518_dec_use(struct i2c_client *client);
+static u16 swap_bytes(u16 val);
+static int gl518_read_value(struct i2c_client *client, u8 reg);
+static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void gl518_update_client(struct i2c_client *client);
+
+static void gl518_update_client_rev00(struct i2c_client *client);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,68)
+static int gl518_update_thread(void *data);
+#endif
+static void gl518_update_iterate(struct i2c_client *client);
+
+static void gl518_vin(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl518_temp(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan_div(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl518_alarms(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void gl518_beep(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan1off(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl518_iterate(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 gl518_driver = {
+	/* name */ "GL518SM sensor chip driver",
+	/* id */ I2C_DRIVERID_GL518,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &gl518_attach_adapter,
+	/* detach_client */ &gl518_detach_client,
+	/* command */ &gl518_command,
+	/* inc_use */ &gl518_inc_use,
+	/* dec_use */ &gl518_dec_use
+};
+
+/* These files are created for each detected GL518. 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 gl518_dir_table_template[] = {
+	{GL518_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_fan},
+	{GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_fan},
+	{GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_temp},
+	{GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_fan_div},
+	{GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_alarms},
+	{GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_beep},
+	{GL518_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_fan1off},
+	{GL518_SYSCTL_ITERATE, "iterate", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl518_iterate},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata gl518_initialized = 0;
+
+/* I choose here for semi-static GL518SM allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+#define MAX_GL518_NR 4
+static struct i2c_client *gl518_list[MAX_GL518_NR];
+
+int gl518_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, gl518_detect);
+}
+
+static int gl518_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct gl518_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("gl518sm.o: gl518_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))
+		    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 gl518_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct gl518_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct gl518_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &gl518_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    (gl518_read_value(new_client, GL518_REG_CHIP_ID) !=
+		     0x80)
+		    || (gl518_read_value(new_client, GL518_REG_CONF) &
+			0x80)) goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = gl518_read_value(new_client, GL518_REG_REVISION);
+		if (i == 0x00)
+			kind = gl518sm_r00;
+		else if (i == 0x80)
+			kind = gl518sm_r80;
+		else {
+			if (kind == 0)
+				printk
+				    ("gl518sm.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == gl518sm_r00) {
+		type_name = "gl518sm";
+		client_name = "GL518SM Revision 0x00 chip";
+	} else if (kind == gl518sm_r80) {
+		type_name = "gl518sm";
+		client_name = "GL518SM Revision 0x80 chip";
+	} else {
+#ifdef DEBUG
+		printk("gl518sm.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->type = kind;
+
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (!gl518_list[i])
+			break;
+	if (i == MAX_GL518_NR) {
+		printk
+		    ("gl518sm.o: No empty slots left, recompile and heighten "
+		     "MAX_GL518_NR!\n");
+		err = -ENOMEM;
+		goto ERROR2;
+	}
+	gl518_list[i] = new_client;
+	new_client->id = i;
+	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 = sensors_register_entry((struct i2c_client *) new_client,
+					type_name,
+					gl518_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the GL518SM chip */
+	data->iterate = 0;
+	data->iterate_lock = 0;
+	data->quit_thread = 0;
+	data->thread = NULL;
+	data->alarm_mask = 0xff;
+	data->voltage[0]=data->voltage[1]=data->voltage[2]=0;
+	gl518_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:
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (new_client == gl518_list[i])
+			gl518_list[i] = NULL;
+      ERROR2:
+      ERROR1:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+
+/* Called when we have found a new GL518SM. It should set limits, etc. */
+void gl518_init_client(struct i2c_client *client)
+{
+	/* Power-on defaults (bit 7=1) */
+	gl518_write_value(client, GL518_REG_CONF, 0x80);
+
+	/* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
+	   standby mode (bit6=0) */
+	gl518_write_value(client, GL518_REG_CONF, 0x04);
+
+	/* Never interrupts */
+	gl518_write_value(client, GL518_REG_MASK, 0x00);
+
+	gl518_write_value(client, GL518_REG_TEMP_HYST,
+			  TEMP_TO_REG(GL518_INIT_TEMP_HYST));
+	gl518_write_value(client, GL518_REG_TEMP_OVER,
+			  TEMP_TO_REG(GL518_INIT_TEMP_OVER));
+	gl518_write_value(client, GL518_REG_MISC, (DIV_TO_REG(2) << 6) |
+			  (DIV_TO_REG(2) << 4));
+	gl518_write_value(client, GL518_REG_FAN_LIMIT,
+			  (FAN_TO_REG(GL518_INIT_FAN_MIN_1, 2) << 8) |
+			  FAN_TO_REG(GL518_INIT_FAN_MIN_2, 2));
+	gl518_write_value(client, GL518_REG_VIN1_LIMIT,
+			  (IN_TO_REG(GL518_INIT_VIN_MAX_1) << 8) |
+			  IN_TO_REG(GL518_INIT_VIN_MIN_1));
+	gl518_write_value(client, GL518_REG_VIN2_LIMIT,
+			  (IN_TO_REG(GL518_INIT_VIN_MAX_2) << 8) |
+			  IN_TO_REG(GL518_INIT_VIN_MIN_2));
+	gl518_write_value(client, GL518_REG_VIN3_LIMIT,
+			  (IN_TO_REG(GL518_INIT_VIN_MAX_3) << 8) |
+			  IN_TO_REG(GL518_INIT_VIN_MIN_3));
+	gl518_write_value(client, GL518_REG_VDD_LIMIT,
+			  (VDD_TO_REG(GL518_INIT_VDD_MAX) << 8) |
+			  VDD_TO_REG(GL518_INIT_VDD_MIN));
+
+	/* Clear status register (bit 5=1), start (bit6=1) */
+	gl518_write_value(client, GL518_REG_CONF, 0x24);
+	gl518_write_value(client, GL518_REG_CONF, 0x44);
+}
+
+int gl518_detach_client(struct i2c_client *client)
+{
+	int err, i;
+	struct gl518_data *data = client->data;
+
+	sensors_deregister_entry(((struct gl518_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("gl518sm.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (client == gl518_list[i])
+			break;
+	if ((i == MAX_GL518_NR)) {
+		printk("gl518sm.o: Client to detach not found.\n");
+		return -ENOENT;
+	}
+	gl518_list[i] = NULL;
+
+	if (data->thread) {
+		data->quit_thread = 1;
+		wake_up_interruptible(&data->wq);
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void gl518_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void gl518_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL518 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int gl518_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return swap_bytes(i2c_smbus_read_word_data(client, reg));
+	else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL518 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return i2c_smbus_write_word_data(client, reg,
+						 swap_bytes(value));
+	else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+void gl518_update_client(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int val;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting gl518 update\n");
+#endif
+
+		data->alarms = gl518_read_value(client, GL518_REG_INT);
+		data->beeps = gl518_read_value(client, GL518_REG_ALARM);
+
+		val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
+		data->voltage_min[0] = val & 0xff;
+		data->voltage_max[0] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
+		data->voltage_min[1] = val & 0xff;
+		data->voltage_max[1] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
+		data->voltage_min[2] = val & 0xff;
+		data->voltage_max[2] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
+		data->voltage_min[3] = val & 0xff;
+		data->voltage_max[3] = (val >> 8) & 0xff;
+
+		val = gl518_read_value(client, GL518_REG_FAN_COUNT);
+		data->fan[0] = (val >> 8) & 0xff;
+		data->fan[1] = val & 0xff;
+
+		val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
+		data->fan_min[0] = (val >> 8) & 0xff;
+		data->fan_min[1] = val & 0xff;
+
+		data->temp = gl518_read_value(client, GL518_REG_TEMP);
+		data->temp_over =
+		    gl518_read_value(client, GL518_REG_TEMP_OVER);
+		data->temp_hyst =
+		    gl518_read_value(client, GL518_REG_TEMP_HYST);
+
+		val = gl518_read_value(client, GL518_REG_MISC);
+		data->fan_div[0] = (val >> 6) & 0x03;
+		data->fan_div[1] = (val >> 4) & 0x03;
+
+		data->alarms &= data->alarm_mask;
+
+		val = gl518_read_value(client, GL518_REG_CONF);
+		data->beep_enable = (val >> 2) & 1;
+
+#ifndef DEBUG_VIN
+		if (data->type != gl518sm_r00) {
+			data->voltage[0] =
+			    gl518_read_value(client, GL518_REG_VDD);
+			data->voltage[1] =
+			    gl518_read_value(client, GL518_REG_VIN1);
+			data->voltage[2] =
+			    gl518_read_value(client, GL518_REG_VIN2);
+			data->voltage[3] =
+			    gl518_read_value(client, GL518_REG_VIN3);
+		} else
+			gl518_update_client_rev00(client);
+#else
+		gl518_update_client_rev00(client);
+#endif
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+/* Here we decide how to run the iteration code.
+   When called, we trigger the iteration and report the last
+   measured voltage. No delay for user apps */
+void gl518_update_client_rev00(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int i;
+
+	if (data->iterate == 1) {	/* 10 sec delay */
+		/* as that update is slow, we consider the data valid for 30 seconds */
+		if (
+		    ((jiffies - data->last_updated_v00 > 30 * HZ)
+		     || (data->alarms & 7)
+		     || (!data->valid)) && (!data->iterate_lock)) {
+			data->iterate_lock = 1;
+			gl518_update_iterate(client);
+			data->iterate_lock = 0;
+		}
+		for (i = 0; i < 4; i++)
+			data->voltage[i] = data->iter_voltage[i];
+	} else if (data->iterate == 2) {	/* show results of last iteration */
+		for (i = 0; i < 4; i++)
+			data->voltage[i] = data->iter_voltage[i];
+		wake_up_interruptible(&data->wq);
+	} else {		/* no iteration */
+		data->voltage[3] =
+		    gl518_read_value(client, GL518_REG_VIN3);
+	}
+}
+
+int gl518_update_thread(void *c)
+{
+	struct i2c_client *client = c;
+	struct gl518_data *data = client->data;
+
+#ifdef __SMP__
+	lock_kernel();
+#endif
+	exit_mm(current);
+	current->session = 1;
+	current->pgrp = 1;
+	sigfillset(&current->blocked);
+	current->fs->umask = 0;
+	strcpy(current->comm, "gl518sm");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+	init_waitqueue_head(&(data->wq));
+#else
+	data->wq = NULL;
+#endif
+	data->thread = current;
+
+#ifdef __SMP__
+	unlock_kernel();
+#endif
+
+	for (;;) {
+		if (!data->iterate_lock) {
+			data->iterate_lock = 1;
+			gl518_update_iterate(client);
+			data->iterate_lock = 0;
+		}
+
+		if ((data->quit_thread) || signal_pending(current))
+			break;
+		interruptible_sleep_on(&data->wq);
+	}
+
+	data->thread = NULL;
+	data->quit_thread = 0;
+	return 0;
+}
+
+/* This updates vdd, vin1, vin2 values by doing slow and multiple
+   comparisons for the GL518SM rev 00 that lacks support for direct
+   reading of these values.   Values are kept in iter_voltage   */
+
+void gl518_update_iterate(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int i, j, loop_more = 1, min[3], max[3], delta[3];
+	int alarm, beeps, irqs;
+
+#define VIN_REG(c) c==0?GL518_REG_VDD_LIMIT:\
+                   c==1?GL518_REG_VIN1_LIMIT:\
+                   GL518_REG_VIN2_LIMIT
+
+	/* disable beeps & irqs for vin0-2 */
+	beeps = gl518_read_value(client, GL518_REG_ALARM);
+	irqs = gl518_read_value(client, GL518_REG_MASK);
+	gl518_write_value(client, GL518_REG_ALARM, beeps & ~0x7);
+	gl518_write_value(client, GL518_REG_MASK, irqs & ~0x7);
+
+	alarm = data->alarms;
+
+	for (i = 0; i < 3; i++) {
+		if (alarm & (1 << i)) {
+			min[i] = 0;
+			max[i] = 127;
+		} else {
+			min[i] = data->voltage_min[i];
+			max[i] =
+			    (data->voltage_max[i] +
+			     data->voltage_min[i]) / 2;
+		}
+		delta[i] = (max[i] - min[i]) / 2;
+	}
+
+	for (j = 0; (j < 10 && loop_more); j++) {
+
+		for (i = 0; i < 3; i++)
+			gl518_write_value(client, VIN_REG(i),
+					  max[i] << 8 | min[i]);
+
+		if ((data->thread) &&
+		    ((data->quit_thread) || signal_pending(current)))
+			goto finish;
+
+		/* we wait now 1.5 seconds before comparing */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ + HZ / 2);
+
+		alarm = gl518_read_value(client, GL518_REG_INT);
+
+#ifdef DEBUG_VIN
+		printk("gl518sm: iteration %2d: %4d%c %4d%c %4d%c\n", j,
+		       max[0], (alarm & 1) ? '!' : ' ',
+		       max[1], (alarm & 2) ? '!' : ' ',
+		       max[2], (alarm & 4) ? '!' : ' ');
+#endif
+
+		for (loop_more = 0, i = 0; i < 3; i++) {
+			if (alarm & (1 << i))
+				max[i] += delta[i];
+			else
+				max[i] -= delta[i];
+
+			if (delta[i])
+				loop_more++;
+			delta[i] >>= 1;
+		}
+
+	}
+
+	for (i = 0; i < 3; i++)
+		if (alarm & (1 << i))
+			max[i]++;
+
+#ifdef DEBUG_VIN
+	printk("gl518sm:    final   :%5d %5d %5d\n", max[0], max[1],
+	       max[2]);
+	printk("gl518sm:    meter   :%5d %5d %5d\n", data->voltage[0],
+	       data->voltage[1], data->voltage[2]);
+#endif
+
+	/* update values, including vin3 */
+	for (i = 0; i < 3; i++) {
+		data->iter_voltage[i] = max[i];
+	}
+	data->iter_voltage[3] = gl518_read_value(client, GL518_REG_VIN3);
+	data->last_updated_v00 = jiffies;
+
+      finish:
+
+	/* reset values */
+	for (i = 0; i < 3; i++) {
+		gl518_write_value(client, VIN_REG(i),
+				  data->voltage_max[i] << 8 | data->
+				  voltage_min[i]);
+	}
+
+	gl518_write_value(client, GL518_REG_ALARM, beeps);
+	gl518_write_value(client, GL518_REG_MASK, irqs);
+
+#undef VIN_REG
+}
+
+void gl518_temp(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_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);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			gl518_write_value(client, GL518_REG_TEMP_OVER,
+					  data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			gl518_write_value(client, GL518_REG_TEMP_HYST,
+					  data->temp_hyst);
+		}
+	}
+}
+
+void gl518_vin(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int nr = ctl_name - GL518_SYSCTL_VDD;
+	int regnr, old = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) :
+		    VDD_FROM_REG(data->voltage_min[nr]);
+		results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) :
+		    VDD_FROM_REG(data->voltage_max[nr]);
+		results[2] = nr ? IN_FROM_REG(data->voltage[nr]) :
+		    VDD_FROM_REG(data->voltage[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		regnr =
+		    nr == 0 ? GL518_REG_VDD_LIMIT : nr ==
+		    1 ? GL518_REG_VIN1_LIMIT : nr ==
+		    2 ? GL518_REG_VIN2_LIMIT : GL518_REG_VIN3_LIMIT;
+		if (*nrels_mag == 1)
+			old = gl518_read_value(client, regnr) & 0xff00;
+		if (*nrels_mag >= 2) {
+			data->voltage_max[nr] =
+			    nr ? IN_TO_REG(results[1]) :
+			    VDD_TO_REG(results[1]);
+			old = data->voltage_max[nr] << 8;
+		}
+		if (*nrels_mag >= 1) {
+			data->voltage_min[nr] =
+			    nr ? IN_TO_REG(results[0]) :
+			    VDD_TO_REG(results[0]);
+			old |= data->voltage_min[nr];
+			gl518_write_value(client, regnr, old);
+		}
+	}
+}
+
+
+void gl518_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int nr = ctl_name - GL518_SYSCTL_FAN1;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_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;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+						       DIV_FROM_REG(data->
+								    fan_div
+								    [nr]));
+			old =
+			    gl518_read_value(client, GL518_REG_FAN_LIMIT);
+
+			if (nr == 0) {
+				old =
+				    (old & 0x00ff) | (data->
+						      fan_min[0] << 8);
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x20;
+				else
+					data->alarm_mask |= 0x20;
+			} else {
+				old = (old & 0xff00) | data->fan_min[1];
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x40;
+				else
+					data->alarm_mask |= 0x40;
+			}
+			gl518_write_value(client, GL518_REG_FAN_LIMIT,
+					  old);
+		}
+	}
+}
+
+
+void gl518_alarms(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void gl518_beep(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			gl518_write_value(client, GL518_REG_CONF,
+					  (gl518_read_value(client,
+							    GL518_REG_CONF)
+					   & 0xfb) | (data->
+						      beep_enable << 2));
+		}
+		if (*nrels_mag >= 2) {
+			data->beeps =
+			    BEEPS_TO_REG(results[1]) & data->alarm_mask;
+			gl518_write_value(client, GL518_REG_ALARM,
+					  data->beeps);
+		}
+	}
+}
+
+
+void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_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 = gl518_read_value(client, GL518_REG_MISC);
+		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 & 0x3f) | (data->fan_div[0] << 6);
+		}
+		gl518_write_value(client, GL518_REG_MISC, old);
+	}
+}
+
+void gl518_fan1off(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl518_read_value(client, GL518_REG_MISC) & 0x08) !=
+		     0);
+		results[1] =
+		    ((gl518_read_value(client, GL518_REG_CONF) & 0x10) !=
+		     0);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl518_read_value(client,
+					     GL518_REG_MISC) & 0xf7;
+			if (results[0])
+				old |= 0x08;
+			gl518_write_value(client, GL518_REG_MISC, old);
+		}
+		if (*nrels_mag >= 2) {
+			old =
+			    gl518_read_value(client,
+					     GL518_REG_CONF) & 0xef;
+			if (results[1])
+				old |= 0x10;
+			gl518_write_value(client, GL518_REG_CONF, old);
+		}
+	}
+}
+
+void gl518_iterate(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int i;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->iterate;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if ((*nrels_mag >= 1) && (data->iterate != results[0])) {
+			data->iterate = results[0];
+			for (i = 0; i < 4; i++) {
+				data->voltage[i] = 0;
+				data->iter_voltage[i] = 0;
+			}
+			data->valid = 0;
+
+			if ((data->iterate != 2) && (data->thread)) {
+				data->quit_thread = 1;
+				wake_up_interruptible(&data->wq);
+			} else if ((data->iterate == 2) && (!data->thread)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+				init_waitqueue_head(&(data->wq));
+#else
+				data->wq = NULL;
+#endif
+				kernel_thread(gl518_update_thread,
+					      (void *) client, 0);
+			}
+		}
+	}
+}
+
+int __init sensors_gl518sm_init(void)
+{
+	int res;
+
+	printk("gl518sm.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	gl518_initialized = 0;
+	if ((res = i2c_add_driver(&gl518_driver))) {
+		printk
+		    ("gl518sm.o: Driver registration failed, module not inserted.\n");
+		gl518_cleanup();
+		return res;
+	}
+	gl518_initialized++;
+	return 0;
+}
+
+int __init gl518_cleanup(void)
+{
+	int res;
+
+	if (gl518_initialized >= 1) {
+		if ((res = i2c_del_driver(&gl518_driver))) {
+			printk
+			    ("gl518.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		gl518_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("GL518SM driver");
+
+int init_module(void)
+{
+	return sensors_gl518sm_init();
+}
+
+int cleanup_module(void)
+{
+	return gl518_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/ddcmon.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/ddcmon.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/ddcmon.c	(revision 933)
@@ -0,0 +1,440 @@
+/*
+    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>
+
+    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/version.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x50, 0x50, 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);
+
+/* Many constants specified below */
+
+/* DDCMON registers */
+#define DDCMON_REG_ID 0x08
+#define DDCMON_REG_SERIAL 0x0C
+#define DDCMON_REG_HORSIZE 0x15
+#define DDCMON_REG_VERSIZE 0x16
+#define DDCMON_REG_TIMINGS 0x23
+#define DDCMON_REG_TIMBASE 0x36
+#define DDCMON_REG_TIMINCR 18
+#define DDCMON_REG_TIMNUM   4
+#define DDCMON_REG_TIMOFFSET 5
+#define DDCMON_REG_CHECKSUM 0x7f
+
+/* Size of DDCMON in bytes */
+#define DDCMON_SIZE 128
+
+/* Each client has this additional data */
+struct ddcmon_data {
+	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 */
+	int memtype;
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_ddcmon_init(void);
+static int __init ddcmon_cleanup(void);
+
+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 int ddcmon_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+
+static void ddcmon_inc_use(struct i2c_client *client);
+static void ddcmon_dec_use(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_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_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,
+	/* command */ &ddcmon_command,
+	/* inc_use */ &ddcmon_inc_use,
+	/* dec_use */ &ddcmon_dec_use
+};
+
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &ddcmon_idcall},
+	{DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &ddcmon_size},
+	{DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &ddcmon_sync},
+	{DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &ddcmon_timings},
+	{DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &ddcmon_serial},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata ddcmon_initialized = 0;
+
+static int ddcmon_id = 0;
+
+int ddcmon_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, ddcmon_detect);
+}
+
+/* This function is called by sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct ddcmon_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct ddcmon_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ddcmon_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+	/* 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);
+
+	new_client->id = ddcmon_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int ddcmon_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+	return 0;
+}
+
+/* No commands defined yet */
+int ddcmon_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void ddcmon_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void ddcmon_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+void ddcmon_update_client(struct i2c_client *client)
+{
+	struct ddcmon_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 300 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		if (i2c_smbus_write_byte(client, 0)) {
+#ifdef DEBUG
+			printk("ddcmon read start has failed!\n");
+#endif
+		}
+		for (i = 0; i < DDCMON_SIZE; i++) {
+			data->data[i] = (u8) i2c_smbus_read_byte(client);
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	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_ID + 1] |
+		             (data->data[DDCMON_REG_ID] << 8) |
+		             (data->data[DDCMON_REG_ID + 3] << 16) |
+		             (data->data[DDCMON_REG_ID + 2] << 24);
+		*nrels_mag = 1;
+	}
+}
+
+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 sync entry */
+		for(i = DDCMON_REG_TIMBASE;
+		    i < DDCMON_REG_TIMBASE +
+		        (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR);
+		    i += DDCMON_REG_TIMINCR) {
+			if(data->data[i] == 0) {
+				for(j = 0; j < 4; j++)
+					results[j] = data->data[i + j +
+					                DDCMON_REG_TIMOFFSET];
+				return;
+			}
+		}
+		for(j = 0; j < 4; j++)
+			results[j] = 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_TIMINGS] |
+		             (data->data[DDCMON_REG_TIMINGS + 1] << 8) |
+		             (data->data[DDCMON_REG_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;
+	}
+}
+
+int __init sensors_ddcmon_init(void)
+{
+	int res;
+
+	printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	ddcmon_initialized = 0;
+	if ((res = i2c_add_driver(&ddcmon_driver))) {
+		printk
+		    ("ddcmon.o: Driver registration failed, module not inserted.\n");
+		ddcmon_cleanup();
+		return res;
+	}
+	ddcmon_initialized++;
+	return 0;
+}
+
+int __init ddcmon_cleanup(void)
+{
+	int res;
+
+	if (ddcmon_initialized >= 1) {
+		if ((res = i2c_del_driver(&ddcmon_driver))) {
+			printk
+			    ("ddcmon.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+	} else
+		ddcmon_initialized--;
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge .com>, "
+	      "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("DDCMON driver");
+
+int init_module(void)
+{
+	return sensors_ddcmon_init();
+}
+
+int cleanup_module(void)
+{
+	return ddcmon_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/adm1021.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/adm1021.c	(revision 960)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/adm1021.c	(revision 960)
@@ -0,0 +1,646 @@
+/*
+    adm1021.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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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_7(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm);
+
+/* adm1021 constants specified below */
+
+/* The adm1021 registers */
+/* Read-only */
+#define ADM1021_REG_TEMP 0x00
+#define ADM1021_REG_REMOTE_TEMP 0x01
+#define ADM1021_REG_STATUS 0x02
+#define ADM1021_REG_MAN_ID 0x0FE	/* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys */
+#define ADM1021_REG_DEV_ID 0x0FF	/* ADM1021 = 0x0X, ADM1023 = 0x3X */
+#define ADM1021_REG_DIE_CODE 0x0FF	/* MAX1617A */
+/* These use different addresses for reading/writing */
+#define ADM1021_REG_CONFIG_R 0x03
+#define ADM1021_REG_CONFIG_W 0x09
+#define ADM1021_REG_CONV_RATE_R 0x04
+#define ADM1021_REG_CONV_RATE_W 0x0A
+/* These are for the ADM1023's additional precision on the remote temp sensor */
+#define ADM1021_REG_REM_TEMP_PREC 0x010
+#define ADM1021_REG_REM_OFFSET 0x011
+#define ADM1021_REG_REM_OFFSET_PREC 0x012
+#define ADM1021_REG_REM_TOS_PREC 0x013
+#define ADM1021_REG_REM_THYST_PREC 0x014
+/* limits */
+#define ADM1021_REG_TOS_R 0x05
+#define ADM1021_REG_TOS_W 0x0B
+#define ADM1021_REG_REMOTE_TOS_R 0x07
+#define ADM1021_REG_REMOTE_TOS_W 0x0D
+#define ADM1021_REG_THYST_R 0x06
+#define ADM1021_REG_THYST_W 0x0C
+#define ADM1021_REG_REMOTE_THYST_R 0x08
+#define ADM1021_REG_REMOTE_THYST_W 0x0E
+/* write-only */
+#define ADM1021_REG_ONESHOT 0x0F
+
+
+/* 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))
+
+/* Initial values */
+
+/* Note: Eventhough I left the low and high limits named os and hyst, 
+they don't quite work like a thermostat the way the LM75 does.  I.e., 
+a lower temp than THYST actuall triggers an alarm instead of 
+clearing it.  Weird, ey?   --Phil  */
+#define adm1021_INIT_TOS 60
+#define adm1021_INIT_THYST 20
+#define adm1021_INIT_REMOTE_TOS 60
+#define adm1021_INIT_REMOTE_THYST 20
+
+/* Each client has this additional data */
+struct adm1021_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 temp, temp_os, temp_hyst;	/* Register values */
+	u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code;
+        /* Special values for ADM1023 only */
+	u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, 
+	   remote_temp_offset, remote_temp_offset_prec;
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_adm1021_init(void);
+static int __init adm1021_cleanup(void);
+static int adm1021_attach_adapter(struct i2c_adapter *adapter);
+static int adm1021_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static void adm1021_init_client(struct i2c_client *client);
+static int adm1021_detach_client(struct i2c_client *client);
+static int adm1021_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void adm1021_inc_use(struct i2c_client *client);
+static void adm1021_dec_use(struct i2c_client *client);
+static int adm1021_read_value(struct i2c_client *client, u8 reg);
+static int adm1021_write_value(struct i2c_client *client, u8 reg,
+			       u16 value);
+static void adm1021_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1021_remote_temp(struct i2c_client *client, int operation,
+				int ctl_name, int *nrels_mag,
+				long *results);
+static void adm1021_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm1021_die_code(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void adm1021_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver adm1021_driver = {
+	/* name */ "ADM1021, MAX1617 sensor driver",
+	/* id */ I2C_DRIVERID_ADM1021,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &adm1021_attach_adapter,
+	/* detach_client */ &adm1021_detach_client,
+	/* command */ &adm1021_command,
+	/* inc_use */ &adm1021_inc_use,
+	/* dec_use */ &adm1021_dec_use
+};
+
+/* These files are created for each detected adm1021. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table adm1021_dir_table_template[] = {
+	{ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_temp},
+	{ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_remote_temp},
+	{ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_die_code},
+	{ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_alarms},
+	{0}
+};
+
+static ctl_table adm1021_max_dir_table_template[] = {
+	{ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_temp},
+	{ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_remote_temp},
+	{ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1021_alarms},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata adm1021_initialized = 0;
+
+/* I choose here for semi-static allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int adm1021_id = 0;
+
+int adm1021_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, adm1021_detect);
+}
+
+static int adm1021_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm1021_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n");
+		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 adm1021_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct adm1021_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct adm1021_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1021_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    (adm1021_read_value(new_client, ADM1021_REG_STATUS) &
+		     0x03) != 0x00)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+
+	if (kind <= 0) {
+		i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
+		if (i == 0x41)
+		  if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
+			kind = adm1023;
+		  else
+			kind = adm1021;
+		else if (i == 0x49)
+			kind = thmc10;
+		else if (i == 0x23)
+			kind = gl523sm;
+		else if ((i == 0x4d) &&
+			 (adm1021_read_value
+			  (new_client, ADM1021_REG_DEV_ID) == 0x01))
+			kind = max1617a;
+		/* LM84 Mfr ID in a different place */
+		else
+		    if (adm1021_read_value
+			(new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
+			kind = lm84;
+		else
+			kind = max1617;
+	}
+
+	if (kind == max1617) {
+		type_name = "max1617";
+		client_name = "MAX1617 chip";
+	} else if (kind == max1617a) {
+		type_name = "max1617a";
+		client_name = "MAX1617A chip";
+	} else if (kind == adm1021) {
+		type_name = "adm1021";
+		client_name = "ADM1021 chip";
+	} else if (kind == adm1023) {
+		type_name = "adm1023";
+		client_name = "ADM1023 chip";
+	} else if (kind == thmc10) {
+		type_name = "thmc10";
+		client_name = "THMC10 chip";
+	} else {
+#ifdef DEBUG
+		printk("adm1021.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->type = kind;
+
+	new_client->id = adm1021_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					data->type ==
+					adm1021 ?
+					adm1021_dir_table_template :
+					adm1021_max_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM1021 chip */
+	adm1021_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(new_client);
+      ERROR0:
+	return err;
+}
+
+void adm1021_init_client(struct i2c_client *client)
+{
+	/* Initialize the adm1021 chip */
+	adm1021_write_value(client, ADM1021_REG_TOS_W,
+			    TEMP_TO_REG(adm1021_INIT_TOS));
+	adm1021_write_value(client, ADM1021_REG_THYST_W,
+			    TEMP_TO_REG(adm1021_INIT_THYST));
+	adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W,
+			    TEMP_TO_REG(adm1021_INIT_REMOTE_TOS));
+	adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W,
+			    TEMP_TO_REG(adm1021_INIT_REMOTE_THYST));
+	/* Enable ADC and disable suspend mode */
+	adm1021_write_value(client, ADM1021_REG_CONFIG_W, 0);
+	/* Set Conversion rate to 1/sec (this can be tinkered with) */
+	adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04);
+}
+
+int adm1021_detach_client(struct i2c_client *client)
+{
+
+	int err;
+
+	sensors_deregister_entry(((struct adm1021_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm1021.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+
+}
+
+
+/* No commands defined yet */
+int adm1021_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void adm1021_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void adm1021_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* All registers are byte-sized */
+int adm1021_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+void adm1021_update_client(struct i2c_client *client)
+{
+	struct adm1021_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 adm1021 update\n");
+#endif
+
+		data->temp = adm1021_read_value(client, ADM1021_REG_TEMP);
+		data->temp_os =
+		    adm1021_read_value(client, ADM1021_REG_TOS_R);
+		data->temp_hyst =
+		    adm1021_read_value(client, ADM1021_REG_THYST_R);
+		data->remote_temp =
+		    adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
+		data->remote_temp_os =
+		    adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
+		data->remote_temp_hyst =
+		    adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
+		data->alarms =
+		    adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec;
+		if (data->type == adm1021)
+			data->die_code =
+			    adm1021_read_value(client,
+					       ADM1021_REG_DIE_CODE);
+		if (data->type == adm1023) {
+		  data->remote_temp_prec =
+		    adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
+		  data->remote_temp_os_prec =
+		    adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
+		  data->remote_temp_hyst_prec =
+		    adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
+		  data->remote_temp_offset =
+		    adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
+		  data->remote_temp_offset_prec =
+		    adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void adm1021_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_os);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = TEMP_TO_REG(results[0]);
+			adm1021_write_value(client, ADM1021_REG_TOS_W,
+					    data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			adm1021_write_value(client, ADM1021_REG_THYST_W,
+					    data->temp_hyst);
+		}
+	}
+}
+
+void adm1021_remote_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results)
+{
+int prec=0;
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		if (data->type == adm1023) { *nrels_mag = 3; }
+                 else { *nrels_mag = 0; }
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_temp_os);
+		results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
+		results[2] = TEMP_FROM_REG(data->remote_temp);
+		if (data->type == adm1023) {
+		  results[0]=results[0]*1000 + 
+		   ((data->remote_temp_os_prec >> 5) * 125);
+		  results[1]=results[1]*1000 + 
+		   ((data->remote_temp_hyst_prec >> 5) * 125);
+		  results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + 
+                   ((data->remote_temp_offset_prec >> 5) * 125);
+		  results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + 
+		   ((data->remote_temp_prec >> 5) * 125);
+ 		  *nrels_mag = 4;
+		} else {
+ 		  *nrels_mag = 3;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (data->type == adm1023) {
+			  prec=((results[0]-((results[0]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_TOS_PREC,
+                                            prec);
+			  results[0]=results[0]/1000;
+			  data->remote_temp_os_prec=prec;
+			}
+			data->remote_temp_os = TEMP_TO_REG(results[0]);
+			adm1021_write_value(client,
+					    ADM1021_REG_REMOTE_TOS_W,
+					    data->remote_temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			if (data->type == adm1023) {
+			  prec=((results[1]-((results[1]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_THYST_PREC,
+                                            prec);
+			  results[1]=results[1]/1000;
+			  data->remote_temp_hyst_prec=prec;
+			}
+			data->remote_temp_hyst = TEMP_TO_REG(results[1]);
+			adm1021_write_value(client,
+					    ADM1021_REG_REMOTE_THYST_W,
+					    data->remote_temp_hyst);
+		}
+		if (*nrels_mag >= 3) {
+			if (data->type == adm1023) {
+			  prec=((results[2]-((results[2]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_OFFSET_PREC,
+                                            prec);
+			  results[2]=results[2]/1000;
+			  data->remote_temp_offset_prec=prec;
+			  data->remote_temp_offset=results[2];
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_OFFSET,
+                                            data->remote_temp_offset);
+			}
+		}
+	}
+}
+
+void adm1021_die_code(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = data->die_code;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* Can't write to it */
+	}
+}
+
+void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* Can't write to it */
+	}
+}
+
+int __init sensors_adm1021_init(void)
+{
+	int res;
+
+	printk("adm1021.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	adm1021_initialized = 0;
+	if ((res = i2c_add_driver(&adm1021_driver))) {
+		printk
+		    ("adm1021.o: Driver registration failed, module not inserted.\n");
+		adm1021_cleanup();
+		return res;
+	}
+	adm1021_initialized++;
+	return 0;
+}
+
+int __init adm1021_cleanup(void)
+{
+	int res;
+
+	if (adm1021_initialized >= 1) {
+		if ((res = i2c_del_driver(&adm1021_driver))) {
+			printk
+			    ("adm1021.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		adm1021_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("adm1021 driver");
+
+int init_module(void)
+{
+	return sensors_adm1021_init();
+}
+
+int cleanup_module(void)
+{
+	return adm1021_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/adm1025.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/adm1025.c	(revision 940)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/adm1025.c	(revision 940)
@@ -0,0 +1,752 @@
+/*
+    adm1025.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Add by Gordon Wu <gwu@esoft.com> according to the adm9240.c written by
+    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 the Analog Devices ADM1025. See doc/chips/adm1025 for details */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* 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(adm1025);
+
+/* Many ADM1025 constants specified below */
+
+#define ADM1025_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define ADM1025_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define ADM1025_REG_IN(nr) (0x20 + (nr))
+
+/* The ADM1025 registers */
+#define ADM1025_REG_TEST 0x15
+/* These are all read-only */
+#define ADM1025_REG_2_5V 0x20
+#define ADM1025_REG_VCCP1 0x21
+#define ADM1025_REG_3_3V 0x22
+#define ADM1025_REG_5V 0x23
+#define ADM1025_REG_12V 0x24
+#define ADM1025_REG_VCC 0x25
+#define ADM1025_REG_RTEMP 0x26
+#define ADM1025_REG_TEMP 0x27
+#define ADM1025_REG_COMPANY_ID 0x3E	/* 0x41 for ADM1025 */
+#define ADM1025_REG_DIE_REV 0x3F
+/* These are read/write */
+#define ADM1025_REG_2_5V_HIGH 0x2B
+#define ADM1025_REG_2_5V_LOW 0x2C
+#define ADM1025_REG_VCCP1_HIGH 0x2D
+#define ADM1025_REG_VCCP1_LOW 0x2E
+#define ADM1025_REG_3_3V_HIGH 0x2F
+#define ADM1025_REG_3_3V_LOW 0x30
+#define ADM1025_REG_5V_HIGH 0x31
+#define ADM1025_REG_5V_LOW 0x32
+#define ADM1025_REG_12V_HIGH 0x33
+#define ADM1025_REG_12V_LOW 0x34
+#define ADM1025_REG_VCC_HIGH 0x35
+#define ADM1025_REG_VCC_LOW 0x36
+#define ADM1025_REG_RTEMP_HIGH 0x37	
+#define ADM1025_REG_RTEMP_LOW 0x38	
+#define ADM1025_REG_TEMP_HIGH 0x39
+#define ADM1025_REG_TEMP_LOW 0x3A
+
+#define ADM1025_REG_CONFIG 0x40
+#define ADM1025_REG_INT1_STAT 0x41
+#define ADM1025_REG_INT2_STAT 0x42
+
+#define ADM1025_REG_VID 0x47
+#define ADM1025_REG_VID4 0x49
+
+/* 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) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+#define TEMP_LIMIT_FROM_REG(val) TEMP_FROM_REG(val)
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10), 0, 255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* Initial limits */
+#define ADM1025_INIT_IN_0 190
+#define ADM1025_INIT_IN_1 190
+#define ADM1025_INIT_IN_2 190
+#define ADM1025_INIT_IN_3 190
+#define ADM1025_INIT_IN_4 190
+#define ADM1025_INIT_IN_5 190
+
+#define ADM1025_INIT_IN_PERCENTAGE 10
+
+#define ADM1025_INIT_IN_MIN_0 \
+        (ADM1025_INIT_IN_0 - ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_0 \
+        (ADM1025_INIT_IN_0 + ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MIN_1 \
+        (ADM1025_INIT_IN_1 - ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_1 \
+        (ADM1025_INIT_IN_1 + ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MIN_2 \
+        (ADM1025_INIT_IN_2 - ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_2 \
+        (ADM1025_INIT_IN_2 + ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MIN_3 \
+        (ADM1025_INIT_IN_3 - ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_3 \
+        (ADM1025_INIT_IN_3 + ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MIN_4 \
+        (ADM1025_INIT_IN_4 - ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_4 \
+        (ADM1025_INIT_IN_4 + ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MIN_5 \
+        (ADM1025_INIT_IN_5 - ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100)
+#define ADM1025_INIT_IN_MAX_5 \
+        (ADM1025_INIT_IN_5 + ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100)
+
+#define ADM1025_INIT_RTEMP_MAX 600
+#define ADM1025_INIT_RTEMP_MIN 0
+#define ADM1025_INIT_TEMP_MAX 600
+#define ADM1025_INIT_TEMP_MIN 0
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* For each registered ADM1025, we need to keep some data in memory. That
+   data is pointed to by adm1025_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new adm1025 client is
+   allocated. */
+struct adm1025_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[6];		/* Register value */
+	u8 in_max[6];		/* Register value */
+	u8 in_min[6];		/* Register value */
+	u8 rtemp;		/* Register value */
+	u8 rtemp_max;		/* Register value */
+	u8 rtemp_min;		/* Register value */
+	u8 temp;		/* Register value */
+	u8 temp_max;		/* Register value */
+	u8 temp_min;		/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+	u8 analog_out;		/* Register value */
+	u8 vid;			/* Register value combined */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_adm1025_init(void);
+static int __init adm1025_cleanup(void);
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter);
+static int adm1025_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int adm1025_detach_client(struct i2c_client *client);
+static int adm1025_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void adm1025_inc_use(struct i2c_client *client);
+static void adm1025_dec_use(struct i2c_client *client);
+
+static int adm1025_read_value(struct i2c_client *client, u8 register);
+static int adm1025_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void adm1025_update_client(struct i2c_client *client);
+static void adm1025_init_client(struct i2c_client *client);
+
+
+static void adm1025_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void adm1025_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1025_rm_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1025_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+/*static void adm1025_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);*/
+static void adm1025_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+/* I choose here for semi-static ADM1025 allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int adm1025_id = 0;
+
+static struct i2c_driver adm1025_driver = {
+	/* name */ "ADM1025 sensor driver",
+	/* id */ I2C_DRIVERID_ADM1025,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &adm1025_attach_adapter,
+	/* detach_client */ &adm1025_detach_client,
+	/* command */ &adm1025_command,
+	/* inc_use */ &adm1025_inc_use,
+	/* dec_use */ &adm1025_dec_use
+};
+
+/* Used by adm1025_init/cleanup */
+static int __initdata adm1025_initialized = 0;
+
+/* The /proc/sys entries */
+/* These files are created for each detected ADM1025. 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 adm1025_dir_table_template[] = {
+	{ADM1025_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_RTEMP, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_rm_temp},
+	{ADM1025_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_temp},
+	{ADM1025_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_alarms},
+/*	{ADM1025_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_analog_out},*/
+	{ADM1025_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm1025_vid},
+	{0}
+};
+
+int adm1025_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, adm1025_detect);
+}
+
+static int adm1025_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm1025_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm1025.o: adm1025_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 adm1025_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct adm1025_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct adm1025_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1025_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if((adm1025_read_value(new_client,ADM1025_REG_CONFIG) & 0x80) != 0x00)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = adm1025_read_value(new_client, ADM1025_REG_COMPANY_ID);
+		if (i == 0x41)
+			kind = adm1025;
+		else {
+			if (kind == 0)
+				printk
+				    ("adm1025.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == adm1025) {
+		type_name = "adm1025";
+		client_name = "ADM1025 chip";
+	} else {
+#ifdef DEBUG
+		printk("adm1025.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->type = kind;
+
+	new_client->id = adm1025_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					adm1025_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM1025 chip */
+	adm1025_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int adm1025_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct adm1025_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm1025.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+
+}
+
+/* No commands defined yet */
+int adm1025_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void adm1025_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void adm1025_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+int adm1025_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+int adm1025_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 ADM1025. It should set limits, etc. */
+void adm1025_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 */
+	adm1025_write_value(client, ADM1025_REG_CONFIG, 0x80);
+
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(0),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_0, 0));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(0),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_0, 0));
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(1),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_1, 1));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(1),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_1, 1));
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(2),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_2, 2));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(2),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_2, 2));
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(3),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_3, 3));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(3),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_3, 3));
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(4),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_4, 4));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(4),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_4, 4));
+	adm1025_write_value(client, ADM1025_REG_IN_MIN(5),
+			    IN_TO_REG(ADM1025_INIT_IN_MIN_5, 5));
+	adm1025_write_value(client, ADM1025_REG_IN_MAX(5),
+			    IN_TO_REG(ADM1025_INIT_IN_MAX_5, 5));
+
+	adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH,
+			    TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MAX));
+	adm1025_write_value(client, ADM1025_REG_RTEMP_LOW,
+			    TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MIN));
+	adm1025_write_value(client, ADM1025_REG_TEMP_HIGH,
+			    TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MAX));
+	adm1025_write_value(client, ADM1025_REG_TEMP_LOW,
+			    TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MIN));
+
+	/* Start monitoring */
+	adm1025_write_value(client, ADM1025_REG_CONFIG, 0x01);
+}
+
+void adm1025_update_client(struct i2c_client *client)
+{
+	struct adm1025_data *data = client->data;
+	u8 i;
+
+	down(&data->update_lock);
+
+	if (
+	    (jiffies - data->last_updated >
+	     (data->type == adm1025 ? HZ / 2 : HZ * 2))
+	    || (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting adm1025 update\n");
+#endif
+		for (i = 0; i <= 5; i++) {
+			data->in[i] =
+			    adm1025_read_value(client, ADM1025_REG_IN(i));
+			data->in_min[i] =
+			    adm1025_read_value(client,
+					       ADM1025_REG_IN_MIN(i));
+			data->in_max[i] =
+			    adm1025_read_value(client,
+					       ADM1025_REG_IN_MAX(i));
+		}
+		data->temp =
+		    adm1025_read_value(client, ADM1025_REG_TEMP);
+		data->rtemp =
+		    adm1025_read_value(client, ADM1025_REG_RTEMP);
+#ifdef DEBUG
+		printk("The temp is %2x\n",data->temp);
+#endif
+		data->temp_max =
+		    adm1025_read_value(client, ADM1025_REG_TEMP_HIGH);
+		data->temp_min =
+		    adm1025_read_value(client, ADM1025_REG_TEMP_LOW);
+		data->rtemp_max =
+		    adm1025_read_value(client, ADM1025_REG_RTEMP_HIGH);
+		data->rtemp_min =
+		    adm1025_read_value(client, ADM1025_REG_RTEMP_LOW);
+
+		i = adm1025_read_value(client, ADM1025_REG_VID);
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (adm1025_read_value(client, ADM1025_REG_VID4) & 0x01)
+		    << 4;
+
+		data->alarms =
+		    adm1025_read_value(client,
+				       ADM1025_REG_INT1_STAT) +
+		    (adm1025_read_value(client, ADM1025_REG_INT2_STAT) <<
+		     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 adm1025_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+
+	int scales[6] = { 250, 225, 330, 500, 1200, 330 };
+
+	struct adm1025_data *data = client->data;
+	int nr = ctl_name - ADM1025_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] =
+		    IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192;
+		results[1] =
+		    IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192;
+		results[2] =
+		    IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    IN_TO_REG((results[0] * 192) / scales[nr], nr);
+			adm1025_write_value(client, ADM1025_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] =
+			    IN_TO_REG((results[1] * 192) / scales[nr], nr);
+			adm1025_write_value(client, ADM1025_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+
+void adm1025_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_min);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm1025_write_value(client, ADM1025_REG_TEMP_HIGH,
+					    data->temp_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			adm1025_write_value(client, ADM1025_REG_TEMP_LOW,
+					    data->temp_min);
+		}
+	}
+}
+
+void adm1025_rm_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->rtemp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->rtemp_min);
+		results[2] = TEMP_FROM_REG(data->rtemp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH,
+					    data->rtemp_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			adm1025_write_value(client, ADM1025_REG_RTEMP_LOW,
+					    data->rtemp_min);
+		}
+	}
+}
+
+void adm1025_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+/*
+void adm1025_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_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];
+			adm1025_write_value(client, ADM1025_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+*/
+
+void adm1025_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+int __init sensors_adm1025_init(void)
+{
+	int res;
+
+	printk("adm1025.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	adm1025_initialized = 0;
+
+	if ((res = i2c_add_driver(&adm1025_driver))) {
+		printk
+		    ("adm1025.o: Driver registration failed, module not inserted.\n");
+		adm1025_cleanup();
+		return res;
+	}
+	adm1025_initialized++;
+	return 0;
+}
+
+int __init adm1025_cleanup(void)
+{
+	int res;
+
+	if (adm1025_initialized >= 1) {
+		if ((res = i2c_del_driver(&adm1025_driver))) {
+			printk
+			    ("adm1025.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		adm1025_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("ADM1025 driver");
+
+int init_module(void)
+{
+	return sensors_adm1025_init();
+}
+
+int cleanup_module(void)
+{
+	return adm1025_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/Module.mk
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/Module.mk	(revision 935)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/Module.mk	(revision 935)
@@ -0,0 +1,87 @@
+#  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/chips
+KERNELCHIPSDIR := $(MODULE_DIR)
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+KERNELCHIPSTARGETS := $(MODULE_DIR)/bt869.o $(MODULE_DIR)/gl520sm.o \
+                      $(MODULE_DIR)/matorb.o $(MODULE_DIR)/maxilife.o \
+                      $(MODULE_DIR)/thmc50.o \
+                      $(MODULE_DIR)/via686a.o \
+                      $(MODULE_DIR)/ddcmon.o \
+                      $(MODULE_DIR)/adm1025.o \
+                      $(MODULE_DIR)/lm87.o \
+                      $(MODULE_DIR)/mtp008.o
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_ADM1021=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/adm1021.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_ADM9240=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/adm9240.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_DS1621=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/ds1621.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_EEPROM=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/eeprom.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_GL518SM=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/gl518sm.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_LM75=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/lm75.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_LM78=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/lm78.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_LM80=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/lm80.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_LTC1710=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/ltc1710.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_PCF8574=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/pcf8574.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_SIS5595=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/sis5595.o
+endif
+ifneq ($(shell if grep -q '^CONFIG_SENSORS_W83781D=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELCHIPSTARGETS += $(MODULE_DIR)/w83781d.o
+endif
+
+# Include all dependency files
+INCLUDEFILES += $(KERNELCHIPSTARGETS:.o=.d)
+
+all-kernel-chips: $(KERNELCHIPSTARGETS)
+all :: all-kernel-chips
+
+install-kernel-chips: all-kernel-chips
+	if [ -n "$(KERNELCHIPSTARGETS)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(MODDIR) ; \
+	  $(INSTALL) -o root -g root -m 644 $(KERNELCHIPSTARGETS) $(DESTDIR)$(MODDIR) ;\
+	fi
+
+install :: install-kernel-chips
+
+clean-kernel-chips:
+	$(RM) $(KERNELCHIPSDIR)/*.o $(KERNELCHIPSDIR)/*.d
+clean :: clean-kernel-chips
Index: /lm-sensors/tags/V2-5-5/kernel/chips/bt869.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/bt869.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/bt869.c	(revision 933)
@@ -0,0 +1,576 @@
+/*
+    bt869.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.
+*/
+
+
+#define DEBUG 1
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+
+/* 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 {
+	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 */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_bt869_init(void);
+static int __init bt869_cleanup(void);
+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_command(struct i2c_client *client, unsigned int cmd,
+			 void *arg);
+static void bt869_inc_use(struct i2c_client *client);
+static void bt869_dec_use(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_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_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,
+	/* command */ &bt869_command,
+	/* inc_use */ &bt869_inc_use,
+	/* dec_use */ &bt869_dec_use
+};
+
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_status},
+	{BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_ntsc},
+	{BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_res},
+	{BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_half},
+	{BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_colorbars},
+	{BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &bt869_depth},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata bt869_initialized = 0;
+
+int bt869_id = 0;
+
+int bt869_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, bt869_detect);
+}
+
+/* This function is called by sensors_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; sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct bt869_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data =
+	    (struct bt869_data *) (((struct i2c_client *) new_client) + 1);
+	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);
+
+	new_client->id = bt869_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int bt869_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int bt869_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void bt869_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void bt869_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* All registers are byte-sized.
+   bt869 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+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. */
+int bt869_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+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->depth = 16;
+
+}
+
+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
+/* Set values of device */
+		if ((data->res[0] == 640) && (data->res[1] == 480)) {
+			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)));
+		} else if ((data->res[0] == 800) && (data->res[1] == 600)) {
+			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 {
+			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)));
+			printk
+			    ("bt869.o:  Warning: arbitrary resolutions not supported yet.  Using 640x480.\n");
+			data->res[0] = 640;
+			data->res[1] = 480;
+		}
+		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);
+		bt869_write_value(client, 0xD4, data->half << 6);
+		/* Be a slave to the clock on the Voodoo3 */
+		bt869_write_value(client, 0xba, 0x20);
+		/* depth =16bpp */
+		bt869_write_value(client, 0x0C6, 0x001);
+		bt869_write_value(client, 0xC4, 1);
+
+/* Get status */
+		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_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);
+		}
+	}
+}
+
+int __init sensors_bt869_init(void)
+{
+	int res;
+
+	printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	bt869_initialized = 0;
+	if ((res = i2c_add_driver(&bt869_driver))) {
+		printk
+		    ("bt869.o: Driver registration failed, module not inserted.\n");
+		bt869_cleanup();
+		return res;
+	}
+	bt869_initialized++;
+	return 0;
+}
+
+int __init bt869_cleanup(void)
+{
+	int res;
+
+	if (bt869_initialized >= 1) {
+		if ((res = i2c_del_driver(&bt869_driver))) {
+			printk
+			    ("bt869.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		bt869_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("bt869 driver");
+
+int init_module(void)
+{
+	return sensors_bt869_init();
+}
+
+int cleanup_module(void)
+{
+	return bt869_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/adm9240.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/adm9240.c	(revision 955)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/adm9240.c	(revision 955)
@@ -0,0 +1,874 @@
+/*
+    adm9240.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.
+*/
+
+/* Supports ADM9240, DS1780, and LM81. See doc/chips/adm9240 for details */
+
+/* 
+	A couple notes about the ADM9240:
+
+* It claims to be 'LM7x' register compatible.  This must be in reference
+  to only the LM78, because it is missing stuff to emulate LM75's as well. 
+  (like the Winbond W83781 does)
+ 
+* This driver was written from rev. 0 of the PDF, but it seems well 
+  written and complete (unlike the W83781 which is horrible and has
+  supposidly gone through a few revisions.. rev 0 of that one must
+  have been in crayon on construction paper...)
+  
+* All analog inputs can range from 0 to 2.5, eventhough some inputs are
+  marked as being 5V, 12V, etc.  I don't have any real voltages going 
+  into my prototype, so I'm not sure that things are computed right, 
+  but at least the limits seem to be working OK.
+  
+* Another curiousity is that the fan_div seems to be read-only.  I.e.,
+  any written value to it doesn't seem to make any difference.  The
+  fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases).
+  
+  
+  --Phil
+
+*/
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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_3(adm9240, ds1780, lm81);
+
+/* Many ADM9240 constants specified below */
+
+#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define ADM9240_REG_IN(nr) (0x20 + (nr))
+
+/* The ADM9240 registers */
+#define ADM9240_REG_TEST 0x15
+#define ADM9240_REG_ANALOG_OUT 0x19
+/* These are all read-only */
+#define ADM9240_REG_2_5V 0x20
+#define ADM9240_REG_VCCP1 0x21
+#define ADM9240_REG_3_3V 0x22
+#define ADM9240_REG_5V 0x23
+#define ADM9240_REG_12V 0x24
+#define ADM9240_REG_VCCP2 0x25
+#define ADM9240_REG_TEMP 0x27
+#define ADM9240_REG_FAN1 0x28
+#define ADM9240_REG_FAN2 0x29
+#define ADM9240_REG_COMPANY_ID 0x3E	/* 0x23 for ADM9240; 0xDA for DS1780 */
+				     /* 0x01 for LM81 */
+#define ADM9240_REG_DIE_REV 0x3F
+/* These are read/write */
+#define ADM9240_REG_2_5V_HIGH 0x2B
+#define ADM9240_REG_2_5V_LOW 0x2C
+#define ADM9240_REG_VCCP1_HIGH 0x2D
+#define ADM9240_REG_VCCP1_LOW 0x2E
+#define ADM9240_REG_3_3V_HIGH 0x2F
+#define ADM9240_REG_3_3V_LOW 0x30
+#define ADM9240_REG_5V_HIGH 0x31
+#define ADM9240_REG_5V_LOW 0x32
+#define ADM9240_REG_12V_HIGH 0x33
+#define ADM9240_REG_12V_LOW 0x34
+#define ADM9240_REG_VCCP2_HIGH 0x35
+#define ADM9240_REG_VCCP2_LOW 0x36
+#define ADM9240_REG_TCRIT_LIMIT 0x37	/* LM81 only - not supported */
+#define ADM9240_REG_LOW_LIMIT 0x38	/* LM81 only - not supported */
+#define ADM9240_REG_TOS 0x39
+#define ADM9240_REG_THYST 0x3A
+#define ADM9240_REG_FAN1_MIN 0x3B
+#define ADM9240_REG_FAN2_MIN 0x3C
+
+#define ADM9240_REG_CONFIG 0x40
+#define ADM9240_REG_INT1_STAT 0x41
+#define ADM9240_REG_INT2_STAT 0x42
+#define ADM9240_REG_INT1_MASK 0x43
+#define ADM9240_REG_INT2_MASK 0x44
+
+#define ADM9240_REG_COMPAT 0x45	/* dummy compat. register for other drivers? */
+#define ADM9240_REG_CHASSIS_CLEAR 0x46
+#define ADM9240_REG_VID_FAN_DIV 0x47
+#define ADM9240_REG_I2C_ADDR 0x48
+#define ADM9240_REG_VID4 0x49
+#define ADM9240_REG_TEMP_CONFIG 0x4B
+#define ADM9240_REG_EXTMODE1 0x4C	/* LM81 only - not supported */
+#define ADM9240_REG_EXTMODE2 0x4D	/* LM81 only - not supported */
+
+/* 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) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+extern 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(temp) \
+   ((temp)<256?((((temp)&0x1fe) >> 1) * 10)      + ((temp) & 1) * 5:  \
+               ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5)  \
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10), \
+                                             0,255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* Initial limits */
+#define ADM9240_INIT_IN_0 190
+#define ADM9240_INIT_IN_1 190
+#define ADM9240_INIT_IN_2 190
+#define ADM9240_INIT_IN_3 190
+#define ADM9240_INIT_IN_4 190
+#define ADM9240_INIT_IN_5 190
+
+#define ADM9240_INIT_IN_PERCENTAGE 10
+
+#define ADM9240_INIT_IN_MIN_0 \
+        (ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_0 \
+        (ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MIN_1 \
+        (ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_1 \
+        (ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MIN_2 \
+        (ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_2 \
+        (ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MIN_3 \
+        (ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_3 \
+        (ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MIN_4 \
+        (ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_4 \
+        (ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MIN_5 \
+        (ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
+#define ADM9240_INIT_IN_MAX_5 \
+        (ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
+
+#define ADM9240_INIT_FAN_MIN_1 3000
+#define ADM9240_INIT_FAN_MIN_2 3000
+
+#define ADM9240_INIT_TEMP_OS_MAX 600
+#define ADM9240_INIT_TEMP_OS_HYST 500
+#define ADM9240_INIT_TEMP_HOT_MAX 700
+#define ADM9240_INIT_TEMP_HOT_HYST 600
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* For each registered ADM9240, we need to keep some data in memory. That
+   data is pointed to by adm9240_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new adm9240 client is
+   allocated. */
+struct adm9240_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[6];		/* Register value */
+	u8 in_max[6];		/* Register value */
+	u8 in_min[6];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	int temp;		/* Temp, shifted right */
+	u8 temp_os_max;		/* Register value */
+	u8 temp_os_hyst;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+	u8 analog_out;		/* Register value */
+	u8 vid;			/* Register value combined */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_adm9240_init(void);
+static int __init adm9240_cleanup(void);
+
+static int adm9240_attach_adapter(struct i2c_adapter *adapter);
+static int adm9240_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int adm9240_detach_client(struct i2c_client *client);
+static int adm9240_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void adm9240_inc_use(struct i2c_client *client);
+static void adm9240_dec_use(struct i2c_client *client);
+
+static int adm9240_read_value(struct i2c_client *client, u8 register);
+static int adm9240_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void adm9240_update_client(struct i2c_client *client);
+static void adm9240_init_client(struct i2c_client *client);
+
+
+static void adm9240_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void adm9240_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm9240_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm9240_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm9240_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void adm9240_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void adm9240_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+/* I choose here for semi-static ADM9240 allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int adm9240_id = 0;
+
+static struct i2c_driver adm9240_driver = {
+	/* name */ "ADM9240 sensor driver",
+	/* id */ I2C_DRIVERID_ADM9240,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &adm9240_attach_adapter,
+	/* detach_client */ &adm9240_detach_client,
+	/* command */ &adm9240_command,
+	/* inc_use */ &adm9240_inc_use,
+	/* dec_use */ &adm9240_dec_use
+};
+
+/* Used by adm9240_init/cleanup */
+static int __initdata adm9240_initialized = 0;
+
+/* The /proc/sys entries */
+/* These files are created for each detected ADM9240. 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 adm9240_dir_table_template[] = {
+	{ADM9240_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_fan},
+	{ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_fan},
+	{ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_temp},
+	{ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_fan_div},
+	{ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_alarms},
+	{ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_analog_out},
+	{ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &adm9240_vid},
+	{0}
+};
+
+int adm9240_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, adm9240_detect);
+}
+
+static int adm9240_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm9240_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm9240.o: adm9240_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 adm9240_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct adm9240_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct adm9240_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm9240_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    ((adm9240_read_value
+		      (new_client, ADM9240_REG_CONFIG) & 0x80) != 0x00)
+		    ||
+		    (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR)
+		     != address))
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = adm9240_read_value(new_client, ADM9240_REG_COMPANY_ID);
+		if (i == 0x23)
+			kind = adm9240;
+		else if (i == 0xda)
+			kind = ds1780;
+		else if (i == 0x01)
+			kind = lm81;
+		else {
+			if (kind == 0)
+				printk
+				    ("adm9240.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == adm9240) {
+		type_name = "adm9240";
+		client_name = "ADM9240 chip";
+	} else if (kind == ds1780) {
+		type_name = "ds1780";
+		client_name = "DS1780 chip";
+	} else if (kind == lm81) {
+		type_name = "lm81";
+		client_name = "LM81 chip";
+	} else {
+#ifdef DEBUG
+		printk("adm9240.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->type = kind;
+
+	new_client->id = adm9240_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					adm9240_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM9240 chip */
+	adm9240_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int adm9240_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct adm9240_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm9240.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+
+}
+
+/* No commands defined yet */
+int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void adm9240_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void adm9240_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+int adm9240_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+int adm9240_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 ADM9240. It should set limits, etc. */
+void adm9240_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 */
+	adm9240_write_value(client, ADM9240_REG_CONFIG, 0x80);
+
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(0),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_0, 0));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(0),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_0, 0));
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(1),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_1, 1));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(1),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_1, 1));
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(2),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_2, 2));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(2),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_2, 2));
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(3),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_3, 3));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(3),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_3, 3));
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(4),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_4, 4));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(4),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_4, 4));
+	adm9240_write_value(client, ADM9240_REG_IN_MIN(5),
+			    IN_TO_REG(ADM9240_INIT_IN_MIN_5, 5));
+	adm9240_write_value(client, ADM9240_REG_IN_MAX(5),
+			    IN_TO_REG(ADM9240_INIT_IN_MAX_5, 5));
+	adm9240_write_value(client, ADM9240_REG_FAN1_MIN,
+			    FAN_TO_REG(ADM9240_INIT_FAN_MIN_1, 2));
+	adm9240_write_value(client, ADM9240_REG_FAN2_MIN,
+			    FAN_TO_REG(ADM9240_INIT_FAN_MIN_2, 2));
+	adm9240_write_value(client, ADM9240_REG_TOS,
+			    TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX));
+	adm9240_write_value(client, ADM9240_REG_THYST,
+			    TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST));
+	adm9240_write_value(client, ADM9240_REG_TEMP_CONFIG, 0x00);
+
+	/* Start monitoring */
+	adm9240_write_value(client, ADM9240_REG_CONFIG, 0x01);
+}
+
+void adm9240_update_client(struct i2c_client *client)
+{
+	struct adm9240_data *data = client->data;
+	u8 i;
+
+	down(&data->update_lock);
+
+	if (
+	    (jiffies - data->last_updated >
+	     (data->type == adm9240 ? HZ / 2 : HZ * 2))
+	    || (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting adm9240 update\n");
+#endif
+		for (i = 0; i <= 5; i++) {
+			data->in[i] =
+			    adm9240_read_value(client, ADM9240_REG_IN(i));
+			data->in_min[i] =
+			    adm9240_read_value(client,
+					       ADM9240_REG_IN_MIN(i));
+			data->in_max[i] =
+			    adm9240_read_value(client,
+					       ADM9240_REG_IN_MAX(i));
+		}
+		data->fan[0] =
+		    adm9240_read_value(client, ADM9240_REG_FAN1);
+		data->fan_min[0] =
+		    adm9240_read_value(client, ADM9240_REG_FAN1_MIN);
+		data->fan[1] =
+		    adm9240_read_value(client, ADM9240_REG_FAN2);
+		data->fan_min[1] =
+		    adm9240_read_value(client, ADM9240_REG_FAN2_MIN);
+		data->temp =
+		    (adm9240_read_value(client, ADM9240_REG_TEMP) << 1) +
+		    ((adm9240_read_value
+		      (client, ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7);
+		data->temp_os_max =
+		    adm9240_read_value(client, ADM9240_REG_TOS);
+		data->temp_os_hyst =
+		    adm9240_read_value(client, ADM9240_REG_THYST);
+
+		i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (adm9240_read_value(client, ADM9240_REG_VID4) & 0x01)
+		    << 4;
+
+		data->alarms =
+		    adm9240_read_value(client,
+				       ADM9240_REG_INT1_STAT) +
+		    (adm9240_read_value(client, ADM9240_REG_INT2_STAT) <<
+		     8);
+		data->analog_out =
+		    adm9240_read_value(client, ADM9240_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 adm9240_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+
+	int scales[6] = { 250, 270, 330, 500, 1200, 270 };
+
+	struct adm9240_data *data = client->data;
+	int nr = ctl_name - ADM9240_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] =
+		    IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192;
+		results[1] =
+		    IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192;
+		results[2] =
+		    IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    IN_TO_REG((results[0] * 192) / scales[nr], nr);
+			adm9240_write_value(client, ADM9240_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] =
+			    IN_TO_REG((results[1] * 192) / scales[nr], nr);
+			adm9240_write_value(client, ADM9240_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_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]));
+			adm9240_write_value(client,
+					    nr ==
+					    1 ? ADM9240_REG_FAN1_MIN :
+					    ADM9240_REG_FAN2_MIN,
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm9240_write_value(client, ADM9240_REG_TOS,
+					    data->temp_os_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
+			adm9240_write_value(client, ADM9240_REG_THYST,
+					    data->temp_os_hyst);
+		}
+	}
+}
+
+void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void adm9240_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_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 = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
+		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);
+			adm9240_write_value(client,
+					    ADM9240_REG_VID_FAN_DIV, old);
+		}
+	}
+}
+
+void adm9240_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_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];
+			adm9240_write_value(client, ADM9240_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+
+void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+int __init sensors_adm9240_init(void)
+{
+	int res;
+
+	printk("adm9240.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	adm9240_initialized = 0;
+
+	if ((res = i2c_add_driver(&adm9240_driver))) {
+		printk
+		    ("adm9240.o: Driver registration failed, module not inserted.\n");
+		adm9240_cleanup();
+		return res;
+	}
+	adm9240_initialized++;
+	return 0;
+}
+
+int __init adm9240_cleanup(void)
+{
+	int res;
+
+	if (adm9240_initialized >= 1) {
+		if ((res = i2c_del_driver(&adm9240_driver))) {
+			printk
+			    ("adm9240.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		adm9240_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("ADM9240 driver");
+
+int init_module(void)
+{
+	return sensors_adm9240_init();
+}
+
+int cleanup_module(void)
+{
+	return adm9240_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/ds1621.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/ds1621.c	(revision 935)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/ds1621.c	(revision 935)
@@ -0,0 +1,509 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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 - most aren't used */
+
+/* Config register used for detection         */
+/*  7    6    5    4    3    2    1    0      */
+/* |Done|THF |TLF |NVB | 1  | 0  |POL |1SHOT| */
+#define DS1621_REG_CONFIG_MASK 0x0C
+#define DS1621_REG_CONFIG_VAL 0x08
+
+/* 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))
+
+/* Initial values */
+#define DS1621_INIT_TEMP_OVER 600
+#define DS1621_INIT_TEMP_HYST 0 /* 500 would cause an alarm at room temp. */
+
+/* Each client has this additional data */
+struct ds1621_data {
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_over, temp_hyst;	/* Register values */
+	u8 conf;			/* Register encoding, combined */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_ds1621_init(void);
+static int __init ds1621_cleanup(void);
+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_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+static void ds1621_inc_use(struct i2c_client *client);
+static void ds1621_dec_use(struct i2c_client *client);
+static u16 swap_bytes(u16 val);
+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_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,
+	/* command */ &ds1621_command,
+	/* inc_use */ &ds1621_inc_use,
+	/* dec_use */ &ds1621_dec_use
+};
+
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &ds1621_temp},
+	{DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &ds1621_alarms},
+	{DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &ds1621_enable},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata ds1621_initialized = 0;
+
+static int ds1621_id = 0;
+
+int ds1621_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, ds1621_detect);
+}
+
+/* This function is called by sensors_detect */
+int ds1621_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i, conf;
+	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; sensors_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))
+		    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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct ds1621_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct ds1621_data *) (new_client + 1);
+	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) {
+		conf = i2c_smbus_read_byte_data(new_client,
+						DS1621_REG_CONF);
+		if ((conf & DS1621_REG_CONFIG_MASK)
+		    != DS1621_REG_CONFIG_VAL)
+			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);
+
+	new_client->id = ds1621_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int ds1621_detach_client(struct i2c_client *client)
+{
+	int err;
+
+#ifdef MODULE
+	if (MOD_IN_USE)
+		return -EBUSY;
+#endif
+
+
+	sensors_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);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int ds1621_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void ds1621_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void ds1621_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* 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. */
+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 swap_bytes(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. */
+int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	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,
+						 swap_bytes(value));
+}
+
+void ds1621_init_client(struct i2c_client *client)
+{
+	/* Initialize the DS1621 chip */
+	ds1621_write_value(client, DS1621_REG_TEMP_OVER,
+			 TEMP_TO_REG(DS1621_INIT_TEMP_OVER));
+	ds1621_write_value(client, DS1621_REG_TEMP_HYST,
+			 TEMP_TO_REG(DS1621_INIT_TEMP_HYST));
+	ds1621_write_value(client, DS1621_REG_CONF, 0);
+
+	/* perhaps we should start the continous conversion? For now */
+	/* you got to do that yourself using the "enable" in proc */
+}
+
+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);
+		/* we should wait for the DONE bit... */
+		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);
+
+		/* reset alarms if neccessary */
+		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)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_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);
+		*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 continous conversion */
+	/* there is no data to read so this might hang your SMBus! */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		*nrels_mag = 0;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_read_value(client, DS1621_COM_START);
+			} else {
+				ds1621_read_value(client, DS1621_COM_STOP);
+			}
+		} else {
+			ds1621_read_value(client, DS1621_COM_START);
+		}
+	}
+}
+
+int __init sensors_ds1621_init(void)
+{
+	int res;
+
+	printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	ds1621_initialized = 0;
+	if ((res = i2c_add_driver(&ds1621_driver))) {
+		printk
+	    ("ds1621.o: Driver registration failed, module not inserted.\n");
+		ds1621_cleanup();
+		return res;
+	}
+	ds1621_initialized++;
+	return 0;
+}
+
+int __init ds1621_cleanup(void)
+{
+	int res;
+
+	if (ds1621_initialized >= 1) {
+		if ((res = i2c_del_driver(&ds1621_driver))) {
+			printk
+			    ("ds1621.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		ds1621_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
+MODULE_DESCRIPTION("DS1621 driver");
+
+int init_module(void)
+{
+	return sensors_ds1621_init();
+}
+
+int cleanup_module(void)
+{
+	return ds1621_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/lm80.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/lm80.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/lm80.c	(revision 933)
@@ -0,0 +1,757 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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,nr) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+extern 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)))
+
+extern inline long TEMP_FROM_REG(u16 temp)
+{
+	long res;
+
+	temp = 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 ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* Initial limits */
+#define LM80_INIT_IN_0 190
+#define LM80_INIT_IN_1 190
+#define LM80_INIT_IN_2 190
+#define LM80_INIT_IN_3 190
+#define LM80_INIT_IN_4 190
+#define LM80_INIT_IN_5 190
+#define LM80_INIT_IN_6 190
+
+#define LM80_INIT_IN_PERCENTAGE 10
+
+#define LM80_INIT_IN_MIN_0 \
+        (LM80_INIT_IN_0 - LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_0 \
+        (LM80_INIT_IN_0 + LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_1 \
+        (LM80_INIT_IN_1 - LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_1 \
+        (LM80_INIT_IN_1 + LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_2 \
+        (LM80_INIT_IN_2 - LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_2 \
+        (LM80_INIT_IN_2 + LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_3 \
+        (LM80_INIT_IN_3 - LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_3 \
+        (LM80_INIT_IN_3 + LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_4 \
+        (LM80_INIT_IN_4 - LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_4 \
+        (LM80_INIT_IN_4 + LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_5 \
+        (LM80_INIT_IN_5 - LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_5 \
+        (LM80_INIT_IN_5 + LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MIN_6 \
+        (LM80_INIT_IN_6 - LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100)
+#define LM80_INIT_IN_MAX_6 \
+        (LM80_INIT_IN_6 + LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100)
+
+#define LM80_INIT_FAN_MIN_1 3000
+#define LM80_INIT_FAN_MIN_2 3000
+
+#define LM80_INIT_TEMP_OS_MAX 600
+#define LM80_INIT_TEMP_OS_HYST 500
+#define LM80_INIT_TEMP_HOT_MAX 700
+#define LM80_INIT_TEMP_HOT_HYST 600
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* For each registered LM80, we need to keep some data in memory. That
+   data is pointed to by lm80_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new lm80 client is
+   allocated. */
+struct lm80_data {
+	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 */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_lm80_init(void);
+static int __init lm80_cleanup(void);
+
+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_command(struct i2c_client *client, unsigned int cmd,
+			void *arg);
+static void lm80_inc_use(struct i2c_client *client);
+static void lm80_dec_use(struct i2c_client *client);
+
+static int lm80_read_value(struct i2c_client *client, u8 register);
+static int lm80_write_value(struct i2c_client *client, u8 register,
+			    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 int lm80_id = 0;
+
+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,
+	/* command */ &lm80_command,
+	/* inc_use */ &lm80_inc_use,
+	/* dec_use */ &lm80_dec_use
+};
+
+/* Used by lm80_init/cleanup */
+static int __initdata lm80_initialized = 0;
+
+/* The /proc/sys entries */
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_temp},
+	{LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_fan_div},
+	{LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm80_alarms},
+	{0}
+};
+
+int lm80_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, lm80_detect);
+}
+
+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; sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct lm80_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct lm80_data *) (new_client + 1);
+	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);
+
+	new_client->id = lm80_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int lm80_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int lm80_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void lm80_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void lm80_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+int lm80_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+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. It should set limits, etc. */
+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);
+
+	lm80_write_value(client, LM80_REG_IN_MIN(0),
+			 IN_TO_REG(LM80_INIT_IN_MIN_0, 0));
+	lm80_write_value(client, LM80_REG_IN_MAX(0),
+			 IN_TO_REG(LM80_INIT_IN_MAX_0, 0));
+	lm80_write_value(client, LM80_REG_IN_MIN(1),
+			 IN_TO_REG(LM80_INIT_IN_MIN_1, 1));
+	lm80_write_value(client, LM80_REG_IN_MAX(1),
+			 IN_TO_REG(LM80_INIT_IN_MAX_1, 1));
+	lm80_write_value(client, LM80_REG_IN_MIN(2),
+			 IN_TO_REG(LM80_INIT_IN_MIN_2, 2));
+	lm80_write_value(client, LM80_REG_IN_MAX(2),
+			 IN_TO_REG(LM80_INIT_IN_MAX_2, 2));
+	lm80_write_value(client, LM80_REG_IN_MIN(3),
+			 IN_TO_REG(LM80_INIT_IN_MIN_3, 3));
+	lm80_write_value(client, LM80_REG_IN_MAX(3),
+			 IN_TO_REG(LM80_INIT_IN_MAX_3, 3));
+	lm80_write_value(client, LM80_REG_IN_MIN(4),
+			 IN_TO_REG(LM80_INIT_IN_MIN_4, 4));
+	lm80_write_value(client, LM80_REG_IN_MAX(4),
+			 IN_TO_REG(LM80_INIT_IN_MAX_4, 4));
+	lm80_write_value(client, LM80_REG_IN_MIN(5),
+			 IN_TO_REG(LM80_INIT_IN_MIN_5, 5));
+	lm80_write_value(client, LM80_REG_IN_MAX(5),
+			 IN_TO_REG(LM80_INIT_IN_MAX_5, 5));
+	lm80_write_value(client, LM80_REG_IN_MIN(6),
+			 IN_TO_REG(LM80_INIT_IN_MIN_6, 6));
+	lm80_write_value(client, LM80_REG_IN_MAX(6),
+			 IN_TO_REG(LM80_INIT_IN_MAX_6, 6));
+	lm80_write_value(client, LM80_REG_FAN1_MIN,
+			 FAN_TO_REG(LM80_INIT_FAN_MIN_1, 2));
+	lm80_write_value(client, LM80_REG_FAN2_MIN,
+			 FAN_TO_REG(LM80_INIT_FAN_MIN_2, 2));
+	lm80_write_value(client, LM80_REG_TEMP_HOT_MAX,
+			 TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX));
+	lm80_write_value(client, LM80_REG_TEMP_HOT_HYST,
+			 TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST));
+	lm80_write_value(client, LM80_REG_TEMP_OS_MAX,
+			 TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX));
+	lm80_write_value(client, LM80_REG_TEMP_OS_HYST,
+			 TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST));
+
+	/* Start monitoring */
+	lm80_write_value(client, LM80_REG_CONFIG, 0x01);
+}
+
+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], nr);
+		results[1] = IN_FROM_REG(data->in_max[nr], nr);
+		results[2] = IN_FROM_REG(data->in[nr], nr);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0], nr);
+			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], nr);
+			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] = ALARMS_FROM_REG(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);
+		}
+	}
+}
+
+int __init sensors_lm80_init(void)
+{
+	int res;
+
+	printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	lm80_initialized = 0;
+
+	if ((res = i2c_add_driver(&lm80_driver))) {
+		printk
+		    ("lm80.o: Driver registration failed, module not inserted.\n");
+		lm80_cleanup();
+		return res;
+	}
+	lm80_initialized++;
+	return 0;
+}
+
+int __init lm80_cleanup(void)
+{
+	int res;
+
+	if (lm80_initialized >= 1) {
+		if ((res = i2c_del_driver(&lm80_driver))) {
+			printk
+			    ("lm80.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		lm80_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LM80 driver");
+
+int init_module(void)
+{
+	return sensors_lm80_init();
+}
+
+int cleanup_module(void)
+{
+	return lm80_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/via686a.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/via686a.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/via686a.c	(revision 933)
@@ -0,0 +1,963 @@
+/*
+    via686a.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 1998, 1999,2000  Frodo Looijaard <frodol@dds.nl>,
+                        Kyösti Mälkki <kmalkki@cc.hut.fi>,
+			Mark Studebaker <mdsxyz123@yahoo.com>,
+			and Bob Dougherty <bobd@stanford.edu>
+    (Some conversion-factor data were contributed by Jonathan Teh Soon Yew 
+    <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/pci.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#ifndef PCI_DEVICE_ID_VIA_82C686_4
+#define PCI_DEVICE_ID_VIA_82C686_4 0x3057
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan.
+   Note that we can't determine the ISA address until we have initialized
+   our module */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(via686a);
+
+/*
+   The Via 686a southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+*/
+
+/* Many VIA686A constants specified below */
+
+/* Length of ISA address segment */
+#define VIA686A_EXTENT 0x80
+#define VIA686A_BASE_REG 0x70
+#define VIA686A_ENABLE_REG 0x74
+
+/* The VIA686A registers */
+/* ins numbered 0-4 */
+#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define VIA686A_REG_IN(nr)     (0x22 + (nr))
+
+/* fans numbered 1-2 */
+#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VIA686A_REG_FAN(nr)     (0x28 + (nr))
+
+// the following values are as speced by VIA:
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e };
+
+/* temps numbered 1-3 */
+#define VIA686A_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VIA686A_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VIA686A_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VIA686A_REG_TEMP_LOW1	0x4b	// bits 7-6
+#define VIA686A_REG_TEMP_LOW23	0x49	// 2 = bits 5-4, 3 = bits 7-6
+
+#define VIA686A_REG_ALARM1 0x41
+#define VIA686A_REG_ALARM2 0x42
+#define VIA686A_REG_FANDIV 0x47
+#define VIA686A_REG_CONFIG 0x40
+// The following register sets temp interrupt mode (bits 1-0 for temp1, 
+// 3-2 for temp2, 5-4 for temp3).  Modes are:
+//    00 interrupt stays as long as value is out-of-range
+//    01 interrupt is cleared once register is read (default)
+//    10 comparator mode- like 00, but ignores hysteresis
+//    11 same as 00
+#define VIA686A_REG_TEMP_MODE 0x4b
+// We'll just assume that you want to set all 3 simulataneously:
+#define VIA686A_TEMP_MODE_MASK 0x3F
+#define VIA686A_TEMP_MODE_CONTINUOUS (0x00)
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. */
+
+/********* VOLTAGE CONVERSIONS (Bob Dougherty) ********/
+// From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
+// voltagefactor[0]=1.25/2628; (2628/1.25=2102.4)   // Vccp
+// voltagefactor[1]=1.25/2628; (2628/1.25=2102.4)   // +2.5V
+// voltagefactor[2]=1.67/2628; (2628/1.67=1573.7)   // +3.3V
+// voltagefactor[3]=2.6/2628;  (2628/2.60=1010.8)   // +5V
+// voltagefactor[4]=6.3/2628;  (2628/6.30=417.14)   // +12V
+// in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
+// That is:
+// volts = (25*regVal+133)*factor
+// regVal = (volts/factor-133)/25
+// (These conversions were contributed by Jonathan Teh Soon Yew 
+// <j.teh@iname.com>)
+// 
+// These get us close, but they don't completely agree with what my BIOS 
+// says- they are all a bit low.  But, it all we have to go on...
+extern inline u8 IN_TO_REG(long val, int inNum)
+{
+	// to avoid floating point, we multiply everything by 100.
+	// val is guaranteed to be positive, so we can achieve the effect of 
+	// rounding by (...*10+5)/10.  Note that the *10 is hidden in the 
+	// /250 (which should really be /2500).
+	// At the end, we need to /100 because we *100 everything and we need
+	// to /10 because of the rounding thing, so we /1000.  
+	if (inNum <= 1)
+		return (u8)
+		    SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000, 
+				  0, 255);
+	else if (inNum == 2)
+		return (u8)
+		    SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000, 
+				  0, 255);
+	else if (inNum == 3)
+		return (u8)
+		    SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000, 
+				  0, 255);
+	else
+		return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5)
+					  / 1000, 0, 255);
+}
+
+extern inline long IN_FROM_REG(u8 val, int inNum)
+{
+	// to avoid floating point, we multiply everything by 100.
+	// val is guaranteed to be positive, so we can achieve the effect of
+	// rounding by adding 0.5.  Or, to avoid fp math, we do (...*10+5)/10.
+	// We need to scale with *100 anyway, so no need to /100 at the end.
+	if (inNum <= 1)
+		return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10);
+	else if (inNum == 2)
+		return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10);
+	else if (inNum == 3)
+		return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10);
+	else
+		return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10);
+}
+
+/********* FAN RPM CONVERSIONS ********/
+// Higher register values = slower fans (the fan's strobe gates a counter).
+// But this chip saturates back at 0, not at 255 like all the other chips.
+// So, 0 means 0 RPM
+extern inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
+
+/******** TEMP CONVERSIONS (Bob Dougherty) *********/
+// linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
+//      if(temp<169)
+//              return double(temp)*0.427-32.08;
+//      else if(temp>=169 && temp<=202)
+//              return double(temp)*0.582-58.16;
+//      else
+//              return double(temp)*0.924-127.33;
+//
+// A fifth-order polynomial fits the unofficial data (provided by Alex van 
+// Kaam <darkside@chello.nl>) a bit better.  It also give more reasonable 
+// numbers on my machine (ie. they agree with what my BIOS tells me).  
+// Here's the fifth-order fit to the 8-bit data:
+// temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - 
+//        2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
+//
+// (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for 
+// finding my typos in this formula!)
+//
+// Alas, none of the elegant function-fit solutions will work because we 
+// aren't allowed to use floating point in the kernel and doing it with 
+// integers doesn't rpovide enough precision.  So we'll do boring old 
+// look-up table stuff.  The unofficial data (see below) have effectively 
+// 7-bit resolution (they are rounded to the nearest degree).  I'm assuming 
+// that the transfer function of the device is monotonic and smooth, so a 
+// smooth function fit to the data will allow us to get better precision.  
+// I used the 5th-order poly fit described above and solved for
+// VIA register values 0-255.  I *10 before rounding, so we get tenth-degree 
+// precision.  (I could have done all 1024 values for our 10-bit readings, 
+// but the function is very linear in the useful range (0-80 deg C), so 
+// we'll just use linear interpolation for 10-bit readings.)  So, tempLUT 
+// is the temp at via register values 0-255:
+static const long tempLUT[] =
+    { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
+	    -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
+	    -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
+	    -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
+	    -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
+	    -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
+	    -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
+	    20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
+	    88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
+	    142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
+	    193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
+	    245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
+	    299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
+	    353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
+	    409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
+	    469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
+	    538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
+	    621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
+	    728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
+	    870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
+	    1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
+	    1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
+};
+
+/* the original LUT values from Alex van Kaam <darkside@chello.nl> 
+   (for via register values 12-240):
+{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
+-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
+-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
+-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
+12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
+22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
+33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
+45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
+61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
+85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
+*/
+
+// Here's the reverse LUT.  I got it by doing a 6-th order poly fit (needed
+// an extra term for a good fit to these inverse data!) and then 
+// solving for each temp value from -50 to 110 (the useable range for 
+// this chip).  Here's the fit: 
+// viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 
+// - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
+// Note that n=161:
+static const u8 viaLUT[] =
+    { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
+	    23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
+	    41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
+	    69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
+	    103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
+	    131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
+	    158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
+	    182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
+	    200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+	    214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
+	    225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
+	    233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+	    239, 240
+};
+
+/* Converting temps to (8-bit) hyst and over registers */
+// No interpolation here.  Just check the limits and go.
+// The +5 effectively rounds off properly and the +50 is because 
+// the temps start at -50
+extern inline u8 TEMP_TO_REG(long val)
+{
+	return (u8)
+	    SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 : 
+				  ((val + 5) / 10 + 50))], 0, 255);
+}
+
+/* for 8-bit temperature hyst and over registers */
+// The temp values are already *10, so we don't need to do that.
+// But we _will_ round these off to the nearest degree with (...*10+5)/10
+#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10)
+
+/* for 10-bit temperature readings */
+// You might _think_ this is too long to inline, but's it's really only
+// called once...
+extern inline long TEMP_FROM_REG10(u16 val)
+{
+	// the temp values are already *10, so we don't need to do that.
+	long temp;
+	u16 eightBits = val >> 2;
+	u16 twoBits = val & 3;
+
+	// handle the extremes first (they won't interpolate well! ;-)
+	if (val == 0)
+		return (long) tempLUT[0];
+	if (val == 1023)
+		return (long) tempLUT[255];
+
+	if (twoBits == 0)
+		return (long) tempLUT[eightBits];
+	else {
+		// do some interpolation by multipying the lower and upper
+		// bounds by 25, 50 or 75, then /100.
+		temp = ((25 * (4 - twoBits)) * tempLUT[eightBits]
+			+ (25 * twoBits) * tempLUT[eightBits + 1]);
+		// increase the magnitude by 50 to achieve rounding.
+		if (temp > 0)
+			temp += 50;
+		else
+			temp -= 50;
+		return (temp / 100);
+	}
+}
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* Initial limits */
+#define VIA686A_INIT_IN_0 200
+#define VIA686A_INIT_IN_1 250
+#define VIA686A_INIT_IN_2 330
+#define VIA686A_INIT_IN_3 500
+#define VIA686A_INIT_IN_4 1200
+
+#define VIA686A_INIT_IN_PERCENTAGE 10
+
+#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \
+        * VIA686A_INIT_IN_PERCENTAGE / 100)
+
+#define VIA686A_INIT_FAN_MIN	3000
+
+#define VIA686A_INIT_TEMP_OVER 600
+#define VIA686A_INIT_TEMP_HYST 500
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered VIA686A, we need to keep some data in memory. That
+   data is pointed to by via686a_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new via686a client is
+   allocated. */
+struct via686a_data {
+	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[5];		/* Register value */
+	u8 in_max[5];		/* Register value */
+	u8 in_min[5];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u16 temp[3];		/* Register value 10 bit */
+	u8 temp_over[3];	/* Register value */
+	u8 temp_hyst[3];	/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_via686a_init(void);
+static int __init via686a_cleanup(void);
+
+static int via686a_attach_adapter(struct i2c_adapter *adapter);
+static int via686a_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int via686a_detach_client(struct i2c_client *client);
+static int via686a_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void via686a_inc_use(struct i2c_client *client);
+static void via686a_dec_use(struct i2c_client *client);
+
+static int via686a_read_value(struct i2c_client *client, u8 register);
+static void via686a_write_value(struct i2c_client *client, u8 register,
+				u8 value);
+static void via686a_update_client(struct i2c_client *client);
+static void via686a_init_client(struct i2c_client *client);
+static int via686a_find(int *address);
+
+
+static void via686a_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void via686a_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void via686a_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void via686a_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void via686a_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+static int via686a_id = 0;
+
+/* The driver. I choose to use type i2c_driver, as at is identical to both
+   smbus_driver and isa_driver, and clients could be of either kind */
+static struct i2c_driver via686a_driver = {
+	/* name */ "VIA 686A",
+	/* id */ I2C_DRIVERID_VIA686A,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &via686a_attach_adapter,
+	/* detach_client */ &via686a_detach_client,
+	/* command */ &via686a_command,
+	/* inc_use */ &via686a_inc_use,
+	/* dec_use */ &via686a_dec_use
+};
+
+/* Used by via686a_init/cleanup */
+static int __initdata via686a_initialized = 0;
+
+/* The /proc/sys entries */
+/* These files are created for each detected VIA686A. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table via686a_dir_table_template[] = {
+	{VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_fan},
+	{VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_fan},
+	{VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &via686a_fan_div},
+	{VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &via686a_alarms},
+	{0}
+};
+
+static inline int via686a_read_value(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void via686a_write_value(struct i2c_client *client, u8 reg,
+				       u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+/* This is called when the module is loaded */
+int via686a_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, via686a_detect);
+}
+
+/* Locate chip and get correct base address */
+int via686a_find(int *address)
+{
+	struct pci_dev *s_bridge;
+	u16 val;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	if (!(s_bridge = pci_find_device(PCI_VENDOR_ID_VIA,
+					 PCI_DEVICE_ID_VIA_82C686_4,
+					 NULL))) return -ENODEV;
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, VIA686A_BASE_REG, &val))
+		return -ENODEV;
+	*address = (val & 0xff80);
+	if (*address == 0) {
+		printk("via686a.o: sensors not enabled - upgrade BIOS?\n");
+		return -ENODEV;
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
+		return -ENODEV;
+	if (!(val & 0x01)) {
+		printk("via686a.o: enabling sensors\n");
+		pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
+		                      val | 0x01);
+	}
+	return 0;
+}
+
+int via686a_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct via686a_data *data;
+	int err = 0;
+	const char *type_name = "via686a";
+
+	/* Make sure we are probing the ISA bus!!  */
+	if (!i2c_is_isa_adapter(adapter)) {
+		printk
+		("via686a.o: via686a_detect called for an I2C bus adapter?!?\n");
+		return 0;
+	}
+
+	if (check_region(address, VIA686A_EXTENT)) {
+		printk("via686a.o: region 0x%x already in use!\n",
+		       address);
+		err = -ENODEV;
+		goto ERROR0;
+	}
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct via686a_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct via686a_data *) (new_client + 1);
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &via686a_driver;
+	new_client->flags = 0;
+
+	/* Reserve the ISA region */
+	request_region(address, VIA686A_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put into the global list */
+	strcpy(new_client->name, "Via 686A Integrated Sensors");
+
+	new_client->id = via686a_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry((struct i2c_client *) new_client,
+					type_name,
+					via686a_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the VIA686A chip */
+	via686a_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, VIA686A_EXTENT);
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int via686a_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct via686a_data *) 
+				  (client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		("via686a.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, VIA686A_EXTENT);
+	kfree(client);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int via686a_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void via686a_inc_use(struct i2c_client *client)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void via686a_dec_use(struct i2c_client *client)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* Called when we have found a new VIA686A. Set limits, etc. */
+void via686a_init_client(struct i2c_client *client)
+{
+	int i;
+
+	/* Reset the device */
+	via686a_write_value(client, VIA686A_REG_CONFIG, 0x80);
+
+	via686a_write_value(client, VIA686A_REG_IN_MIN(0),
+			    IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0));
+	via686a_write_value(client, VIA686A_REG_IN_MAX(0),
+			    IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0));
+	via686a_write_value(client, VIA686A_REG_IN_MIN(1),
+			    IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1));
+	via686a_write_value(client, VIA686A_REG_IN_MAX(1),
+			    IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1));
+	via686a_write_value(client, VIA686A_REG_IN_MIN(2),
+			    IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2));
+	via686a_write_value(client, VIA686A_REG_IN_MAX(2),
+			    IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2));
+	via686a_write_value(client, VIA686A_REG_IN_MIN(3),
+			    IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3));
+	via686a_write_value(client, VIA686A_REG_IN_MAX(3),
+			    IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3));
+	via686a_write_value(client, VIA686A_REG_IN_MIN(4),
+			    IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4));
+	via686a_write_value(client, VIA686A_REG_IN_MAX(4),
+			    IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4));
+	via686a_write_value(client, VIA686A_REG_FAN_MIN(1),
+			    FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
+	via686a_write_value(client, VIA686A_REG_FAN_MIN(2),
+			    FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
+	for (i = 1; i <= 3; i++) {
+		via686a_write_value(client, VIA686A_REG_TEMP_OVER(i),
+				    TEMP_TO_REG(VIA686A_INIT_TEMP_OVER));
+		via686a_write_value(client, VIA686A_REG_TEMP_HYST(i),
+				    TEMP_TO_REG(VIA686A_INIT_TEMP_HYST));
+	}
+
+	/* Start monitoring */
+	via686a_write_value(client, VIA686A_REG_CONFIG, 0x01);
+
+	/* Cofigure temp interrupt mode for continuous-interrupt operation */
+	via686a_write_value(client, VIA686A_REG_TEMP_MODE, 
+			    via686a_read_value(client, VIA686A_REG_TEMP_MODE) &
+			    !VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS);
+}
+
+void via686a_update_client(struct i2c_client *client)
+{
+	struct via686a_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 <= 4; i++) {
+			data->in[i] =
+			    via686a_read_value(client, VIA686A_REG_IN(i));
+			data->in_min[i] = via686a_read_value(client,
+							     VIA686A_REG_IN_MIN
+							     (i));
+			data->in_max[i] =
+			    via686a_read_value(client, VIA686A_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] =
+			    via686a_read_value(client, VIA686A_REG_FAN(i));
+			data->fan_min[i - 1] = via686a_read_value(client,
+						     VIA686A_REG_FAN_MIN(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->temp[i - 1] = via686a_read_value(client,
+						 VIA686A_REG_TEMP(i)) << 2;
+			data->temp_over[i - 1] =
+			    via686a_read_value(client,
+					       VIA686A_REG_TEMP_OVER(i));
+			data->temp_hyst[i - 1] =
+			    via686a_read_value(client,
+					       VIA686A_REG_TEMP_HYST(i));
+		}
+		/* add in lower 2 bits 
+		   temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
+		   temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
+		   temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
+		 */
+		data->temp[0] |= (via686a_read_value(client,
+						     VIA686A_REG_TEMP_LOW1)
+				  & 0xc0) >> 6;
+		data->temp[1] |=
+		    (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+		     0x30) >> 4;
+		data->temp[2] |=
+		    (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+		     0xc0) >> 6;
+
+		i = via686a_read_value(client, VIA686A_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms =
+		    via686a_read_value(client,
+				       VIA686A_REG_ALARM1) |
+		    (via686a_read_value(client, VIA686A_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void via686a_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr], nr);
+		results[1] = IN_FROM_REG(data->in_max[nr], nr);
+		results[2] = IN_FROM_REG(data->in[nr], nr);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0], nr);
+			via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1], nr);
+			via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void via686a_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div
+						       [nr - 1]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0], 
+							   DIV_FROM_REG(data->
+							      fan_div[nr -1]));
+			via686a_write_value(client,
+					    VIA686A_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void via686a_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG10(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[0]);
+			via686a_write_value(client,
+					    VIA686A_REG_TEMP_OVER(nr + 1),
+					    data->temp_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			via686a_write_value(client,
+					    VIA686A_REG_TEMP_HYST(nr + 1),
+					    data->temp_hyst[nr]);
+		}
+	}
+}
+
+void via686a_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void via686a_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = via686a_read_value(client, VIA686A_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			via686a_write_value(client, VIA686A_REG_FANDIV,
+					    old);
+		}
+	}
+}
+
+int __init sensors_via686a_init(void)
+{
+	int res, addr;
+
+	printk("via686a.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	via686a_initialized = 0;
+
+	if (via686a_find(&addr)) {
+		printk("via686a.o: No Via 686A sensors found.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	if ((res = i2c_add_driver(&via686a_driver))) {
+		printk("via686a.o: Driver registration failed.\n");
+		via686a_cleanup();
+		return res;
+	}
+	via686a_initialized++;
+	return 0;
+}
+
+int __init via686a_cleanup(void)
+{
+	int res;
+
+	if (via686a_initialized >= 1) {
+		if ((res = i2c_del_driver(&via686a_driver))) {
+			printk
+			    ("via686a.o: Driver deregistration failed.\n");
+			return res;
+		}
+		via686a_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Kyösti Mälkki <kmalkki@cc.hut.fi>, Mark Studebaker <mdsxyz123@yahoo.com>, Bob Dougherty <bobd@stanford.edu>");
+MODULE_DESCRIPTION("VIA 686A Sensor device");
+
+int init_module(void)
+{
+	return sensors_via686a_init();
+}
+
+int cleanup_module(void)
+{
+	return via686a_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/matorb.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/matorb.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/matorb.c	(revision 933)
@@ -0,0 +1,367 @@
+/*
+    matorb.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.
+*/
+
+
+#define DEBUG 1
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 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_1(matorb);
+
+/* Many MATORB constants specified below */
+
+
+/* Each client has this additional data */
+struct matorb_data {
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_matorb_init(void);
+static int __init matorb_cleanup(void);
+static int matorb_attach_adapter(struct i2c_adapter *adapter);
+static int matorb_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void matorb_init_client(struct i2c_client *client);
+static int matorb_detach_client(struct i2c_client *client);
+static int matorb_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+static void matorb_inc_use(struct i2c_client *client);
+static void matorb_dec_use(struct i2c_client *client);
+static int matorb_write_value(struct i2c_client *client, u8 reg,
+			      u16 value);
+static void matorb_disp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void matorb_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver matorb_driver = {
+	/* name */ "Matrix Orbital LCD driver",
+	/* id */ I2C_DRIVERID_MATORB,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &matorb_attach_adapter,
+	/* detach_client */ &matorb_detach_client,
+	/* command */ &matorb_command,
+	/* inc_use */ &matorb_inc_use,
+	/* dec_use */ &matorb_dec_use
+};
+
+/* These files are created for each detected MATORB. 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 matorb_dir_table_template[] = {
+	{MATORB_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &matorb_disp},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata matorb_initialized = 0;
+
+static int matorb_id = 0;
+
+int matorb_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, matorb_detect);
+}
+
+/* This function is called by sensors_detect */
+int matorb_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct matorb_data *data;
+	int err = 0;
+	const char *type_name = "matorb";
+	const char *client_name = "matorb";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("matorb.o: matorb_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_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 matorb_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct matorb_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct matorb_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &matorb_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	cur = i2c_smbus_write_byte_data(new_client, 0x0FE, 0x58);	/* clear screen */
+
+	printk("matorb.o: debug detect 0x%X\n", cur);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = matorb_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client, type_name,
+					matorb_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	matorb_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:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int matorb_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct matorb_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("matorb.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int matorb_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void matorb_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void matorb_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+#if 0
+/* All registers are word-sized, except for the configuration register.
+   MATORB uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int matorb_read_value(struct i2c_client *client, u8 reg)
+{
+	return -1;		/* Doesn't support reads */
+}
+#endif
+
+/* All registers are word-sized, except for the configuration register.
+   MATORB uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int matorb_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == 0) {
+		return i2c_smbus_write_byte(client, value);
+	} else {
+		return i2c_smbus_write_byte_data(client, reg, value);
+	}
+}
+
+void matorb_init_client(struct i2c_client *client)
+{
+	/* Initialize the MATORB chip */
+}
+
+void matorb_update_client(struct i2c_client *client)
+{
+	struct matorb_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 matorb update\n");
+#endif
+
+/* nothing yet */
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void matorb_disp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	int i;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		matorb_update_client(client);
+		results[0] = 0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i = 1; i <= *nrels_mag; i++) {
+			matorb_write_value(client, 0, results[i - 1]);
+		}
+	}
+}
+
+int __init sensors_matorb_init(void)
+{
+	int res;
+
+	printk("matorb.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	matorb_initialized = 0;
+	if ((res = i2c_add_driver(&matorb_driver))) {
+		printk
+		    ("matorb.o: Driver registration failed, module not inserted.\n");
+		matorb_cleanup();
+		return res;
+	}
+	matorb_initialized++;
+	return 0;
+}
+
+int __init matorb_cleanup(void)
+{
+	int res;
+
+	if (matorb_initialized >= 1) {
+		if ((res = i2c_del_driver(&matorb_driver))) {
+			printk
+			    ("matorb.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		matorb_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("MATORB driver");
+
+int init_module(void)
+{
+	return sensors_matorb_init();
+}
+
+int cleanup_module(void)
+{
+	return matorb_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/lm75.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/lm75.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/lm75.c	(revision 933)
@@ -0,0 +1,436 @@
+/*
+    lm75.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.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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(lm75);
+
+/* Many LM75 constants specified below */
+
+/* The LM75 registers */
+#define LM75_REG_TEMP 0x00
+#define LM75_REG_CONF 0x01
+#define LM75_REG_TEMP_HYST 0x02
+#define LM75_REG_TEMP_OS 0x03
+
+/* 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))
+
+/* Initial values */
+#define LM75_INIT_TEMP_OS 600
+#define LM75_INIT_TEMP_HYST 500
+
+/* Each client has this additional data */
+struct lm75_data {
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_os, temp_hyst;	/* Register values */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_lm75_init(void);
+static int __init lm75_cleanup(void);
+static int lm75_attach_adapter(struct i2c_adapter *adapter);
+static int lm75_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static void lm75_init_client(struct i2c_client *client);
+static int lm75_detach_client(struct i2c_client *client);
+static int lm75_command(struct i2c_client *client, unsigned int cmd,
+			void *arg);
+static void lm75_inc_use(struct i2c_client *client);
+static void lm75_dec_use(struct i2c_client *client);
+static u16 swap_bytes(u16 val);
+static int lm75_read_value(struct i2c_client *client, u8 reg);
+static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void lm75_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm75_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm75_driver = {
+	/* name */ "LM75 sensor chip driver",
+	/* id */ I2C_DRIVERID_LM75,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &lm75_attach_adapter,
+	/* detach_client */ &lm75_detach_client,
+	/* command */ &lm75_command,
+	/* inc_use */ &lm75_inc_use,
+	/* dec_use */ &lm75_dec_use
+};
+
+/* These files are created for each detected LM75. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table lm75_dir_table_template[] = {
+	{LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm75_temp},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata lm75_initialized = 0;
+
+static int lm75_id = 0;
+
+int lm75_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, lm75_detect);
+}
+
+/* This function is called by sensors_detect */
+int lm75_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i, cur, conf, hyst, os;
+	struct i2c_client *new_client;
+	struct lm75_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     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 lm75_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct lm75_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct lm75_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm75_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (kind < 0) {
+		cur = i2c_smbus_read_word_data(new_client, 0);
+		conf = i2c_smbus_read_byte_data(new_client, 1);
+		hyst = i2c_smbus_read_word_data(new_client, 2);
+		os = i2c_smbus_read_word_data(new_client, 3);
+		for (i = 0; i <= 0x1f; i++)
+			if (
+			    (i2c_smbus_read_byte_data
+			     (new_client, i * 8 + 1) != conf)
+			    ||
+			    (i2c_smbus_read_word_data
+			     (new_client, i * 8 + 2) != hyst)
+			    ||
+			    (i2c_smbus_read_word_data
+			     (new_client, i * 8 + 3) != os))
+				goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = lm75;
+
+	if (kind == lm75) {
+		type_name = "lm75";
+		client_name = "LM75 chip";
+	} else {
+#ifdef DEBUG
+		printk("lm75.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);
+
+	new_client->id = lm75_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client, type_name,
+					lm75_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	lm75_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int lm75_detach_client(struct i2c_client *client)
+{
+	int err;
+
+#ifdef MODULE
+	if (MOD_IN_USE)
+		return -EBUSY;
+#endif
+
+
+	sensors_deregister_entry(((struct lm75_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm75.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int lm75_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void lm75_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void lm75_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* All registers are word-sized, except for the configuration register.
+   LM75 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int lm75_read_value(struct i2c_client *client, u8 reg)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return swap_bytes(i2c_smbus_read_word_data(client, reg));
+}
+
+/* All registers are word-sized, except for the configuration register.
+   LM75 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg,
+						 swap_bytes(value));
+}
+
+void lm75_init_client(struct i2c_client *client)
+{
+	/* Initialize the LM75 chip */
+	lm75_write_value(client, LM75_REG_TEMP_OS,
+			 TEMP_TO_REG(LM75_INIT_TEMP_OS));
+	lm75_write_value(client, LM75_REG_TEMP_HYST,
+			 TEMP_TO_REG(LM75_INIT_TEMP_HYST));
+	lm75_write_value(client, LM75_REG_CONF, 0);
+}
+
+void lm75_update_client(struct i2c_client *client)
+{
+	struct lm75_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 lm75 update\n");
+#endif
+
+		data->temp = lm75_read_value(client, LM75_REG_TEMP);
+		data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS);
+		data->temp_hyst =
+		    lm75_read_value(client, LM75_REG_TEMP_HYST);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void lm75_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm75_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm75_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_os);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = TEMP_TO_REG(results[0]);
+			lm75_write_value(client, LM75_REG_TEMP_OS,
+					 data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			lm75_write_value(client, LM75_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+int __init sensors_lm75_init(void)
+{
+	int res;
+
+	printk("lm75.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	lm75_initialized = 0;
+	if ((res = i2c_add_driver(&lm75_driver))) {
+		printk
+		    ("lm75.o: Driver registration failed, module not inserted.\n");
+		lm75_cleanup();
+		return res;
+	}
+	lm75_initialized++;
+	return 0;
+}
+
+int __init lm75_cleanup(void)
+{
+	int res;
+
+	if (lm75_initialized >= 1) {
+		if ((res = i2c_del_driver(&lm75_driver))) {
+			printk
+			    ("lm75.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		lm75_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM75 driver");
+
+int init_module(void)
+{
+	return sensors_lm75_init();
+}
+
+int cleanup_module(void)
+{
+	return lm75_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/w83781d.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/w83781d.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/w83781d.c	(revision 933)
@@ -0,0 +1,1954 @@
+/*
+    w83781d.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>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Supports following chips:
+
+    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
+    as99127f	7	3	1?	3	0x20	0x12c3	yes	no
+    w83781d	7	3	0	3	0x10	0x5ca3	yes	yes
+    w83627hf	9	3	2	3	0x20	0x5ca3	yes	yes(LPC)
+    w83782d	9	3	2-4	3	0x30	0x5ca3	yes	yes
+    w83783s	5-6	3	2	1-2	0x40	0x5ca3	yes	no
+
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* RT Table support #defined so we can take it out if it gets bothersome */
+#define W83781D_RT 1
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_5(w83781d, w83782d, w83783s, w83627hf, as99127f);
+
+/* Many W83781D constants specified below */
+
+/* Length of ISA address segment */
+#define W83781D_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define W83781D_ADDR_REG_OFFSET 5
+#define W83781D_DATA_REG_OFFSET 6
+
+/* The W83781D registers */
+/* The W83782D registers for nr=7,8 are in bank 5 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+					   (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+					   (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr)     ((nr < 7) ? (0x20 + (nr)) : \
+					   (0x550 + (nr) - 7))
+
+#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr))
+#define W83781D_REG_FAN(nr) (0x27 + (nr))
+
+#define W83781D_REG_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 0x41
+#define W83781D_REG_ALARM2 0x42
+#define W83781D_REG_ALARM3 0x450	/* not on W83781D */
+
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453	/* not on W83781D */
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+/* 782D/783S only */
+#define W83781D_REG_VBAT 0x5D
+
+/* PWM 782D (1-4) and 783S (1-2) only */
+#define W83781D_REG_PWM1 0x5B	/* 782d and 783s/627hf datasheets disagree */
+				/* on which is which; */
+#define W83781D_REG_PWM2 0x5A	/* We follow the 782d convention here, */
+				/* However 782d is probably wrong. */
+#define W83781D_REG_PWM3 0x5E
+#define W83781D_REG_PWM4 0x5F
+#define W83781D_REG_PWMCLK12 0x5C
+#define W83781D_REG_PWMCLK34 0x45C
+static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2,
+	W83781D_REG_PWM3, W83781D_REG_PWM4
+};
+#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1])
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* The following are undocumented in the data sheets however we
+   received the information in an email from Winbond tech support */
+/* Sensor selection - not on 781d */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+#define W83781D_DEFAULT_BETA 3435
+
+/* RT Table registers */
+#define W83781D_REG_RT_IDX 0x50
+#define W83781D_REG_RT_VAL 0x51
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) * 16) / 10)
+
+extern 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);
+}
+extern inline u8 AS_FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((810000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+#define AS_FAN_FROM_REG(val,div) \
+                              ((val)==0?-1:(val)==255?0:810000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_ADD_TO_REG(val)   (SENSORS_LIMIT(((((val) + 2) / 5) << 7),\
+                                              0,0xffff))
+#define TEMP_ADD_FROM_REG(val) (((val) >> 7) * 5)
+
+#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val) + 2)*4)/10) \
+                                               << 7),0,0xffff))
+#define AS99127_TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 10) / 4)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+#define ALARMS_FROM_REG(val) (val)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define BEEPS_FROM_REG(val) (val)
+#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))
+
+extern inline u8 DIV_TO_REG(long val, enum chips type)
+{
+	int i;
+	val = SENSORS_LIMIT(val, 1,
+		((type == w83781d || type == as99127f) ? 8 : 128)) >> 1;
+	for (i = 0; i < 6; i++) {
+		if (val == 0)
+			break;
+		val >>= 1;
+	}
+	return ((u8) i);
+}
+
+/* Initial limits */
+#define W83781D_INIT_IN_0 (vid==350?280:vid)
+#define W83781D_INIT_IN_1 (vid==350?280:vid)
+#define W83781D_INIT_IN_2 330
+#define W83781D_INIT_IN_3 (((500)   * 100)/168)
+#define W83781D_INIT_IN_4 (((1200)  * 10)/38)
+#define W83781D_INIT_IN_5 (((-1200) * -604)/2100)
+#define W83781D_INIT_IN_6 (((-500)  * -604)/909)
+#define W83781D_INIT_IN_7 (((500)   * 100)/168)
+#define W83781D_INIT_IN_8 300
+/* Initial limits for 782d/783s negative voltages */
+/* Note level shift. Change min/max below if you change these. */
+#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514)
+#define W83782D_INIT_IN_6 ((( (-500)  + 771) * 100)/314)
+
+#define W83781D_INIT_IN_PERCENTAGE 10
+
+#define W83781D_INIT_IN_MIN_0 \
+        (W83781D_INIT_IN_0 - W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_0 \
+        (W83781D_INIT_IN_0 + W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_1 \
+        (W83781D_INIT_IN_1 - W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_1 \
+        (W83781D_INIT_IN_1 + W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_2 \
+        (W83781D_INIT_IN_2 - W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_2 \
+        (W83781D_INIT_IN_2 + W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_3 \
+        (W83781D_INIT_IN_3 - W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_3 \
+        (W83781D_INIT_IN_3 + W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_4 \
+        (W83781D_INIT_IN_4 - W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_4 \
+        (W83781D_INIT_IN_4 + W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_5 \
+        (W83781D_INIT_IN_5 - W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_5 \
+        (W83781D_INIT_IN_5 + W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_6 \
+        (W83781D_INIT_IN_6 - W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_6 \
+        (W83781D_INIT_IN_6 + W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_7 \
+        (W83781D_INIT_IN_7 - W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_7 \
+        (W83781D_INIT_IN_7 + W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MIN_8 \
+        (W83781D_INIT_IN_8 - W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+#define W83781D_INIT_IN_MAX_8 \
+        (W83781D_INIT_IN_8 + W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \
+         / 100)
+/* Initial limits for 782d/783s negative voltages */
+/* These aren't direct multiples because of level shift */
+/* Beware going negative - check */
+#define W83782D_INIT_IN_MIN_5_TMP \
+        (((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
+#define W83782D_INIT_IN_MIN_5 \
+        ((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0)
+#define W83782D_INIT_IN_MAX_5 \
+        (((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
+#define W83782D_INIT_IN_MIN_6_TMP \
+        ((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) +  (771 * 100))/314)
+#define W83782D_INIT_IN_MIN_6 \
+        ((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0)
+#define W83782D_INIT_IN_MAX_6 \
+        ((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) +  (771 * 100))/314)
+
+#define W83781D_INIT_FAN_MIN_1 3000
+#define W83781D_INIT_FAN_MIN_2 3000
+#define W83781D_INIT_FAN_MIN_3 3000
+
+#define W83781D_INIT_TEMP_OVER 600
+#define W83781D_INIT_TEMP_HYST 500
+#define W83781D_INIT_TEMP2_OVER 600
+#define W83781D_INIT_TEMP2_HYST 500
+#define W83781D_INIT_TEMP3_OVER 600
+#define W83781D_INIT_TEMP3_HYST 500
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* There are some complications in a module like this. First off, W83781D chips
+   may be both present on the SMBus and the ISA bus, and we have to handle
+   those cases separately at some places. Second, there might be several
+   W83781D chips available (well, actually, that is probably never done; but
+   it is a clean illustration of how to handle a case like that). Finally,
+   a specific chip may be attached to *both* ISA and SMBus, and we would
+   not like to detect it double. Fortunately, in the case of the W83781D at
+   least, a register tells us what SMBus address we are on, so that helps
+   a bit - except if there could be more than one SMBus. Groan. No solution
+   for this yet. */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered W83781D, we need to keep some data in memory. That
+   data is pointed to by w83781d_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new w83781d client is
+   allocated. */
+struct w83781d_data {
+	struct semaphore lock;
+	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;	/* for secondary I2C addresses */
+	/* pointer to array of 2 subclients */
+
+	u8 in[9];		/* Register value - 8 & 9 for 782D only */
+	u8 in_max[9];		/* Register value - 8 & 9 for 782D only */
+	u8 in_min[9];		/* Register value - 8 & 9 for 782D only */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp;
+	u8 temp_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[4];		/* Register value */
+	u16 sens[3];		/* 782D/783S only.
+				   1 = pentium diode; 2 = 3904 diode;
+				   3000-5000 = thermistor beta.
+				   Default = 3435. 
+				   Other Betas unimplemented */
+#ifdef W83781D_RT
+	u8 rt[3][32];		/* Register value */
+#endif
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_w83781d_init(void);
+static int __init w83781d_cleanup(void);
+
+static int w83781d_attach_adapter(struct i2c_adapter *adapter);
+static int w83781d_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int w83781d_detach_client(struct i2c_client *client);
+static int w83781d_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void w83781d_inc_use(struct i2c_client *client);
+static void w83781d_dec_use(struct i2c_client *client);
+
+static int w83781d_read_value(struct i2c_client *client, u16 register);
+static int w83781d_write_value(struct i2c_client *client, u16 register,
+			       u16 value);
+static void w83781d_update_client(struct i2c_client *client);
+static void w83781d_init_client(struct i2c_client *client);
+
+
+static void w83781d_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void w83781d_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83781d_temp_add(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void w83781d_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void w83781d_beep(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83781d_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83781d_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_sens(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+#ifdef W83781D_RT
+static void w83781d_rt(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#endif
+static u16 swap_bytes(u16 val);
+
+static int w83781d_id = 0;
+
+static struct i2c_driver w83781d_driver = {
+	/* name */ "W83781D sensor driver",
+	/* id */ I2C_DRIVERID_W83781D,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &w83781d_attach_adapter,
+	/* detach_client */ &w83781d_detach_client,
+	/* command */ &w83781d_command,
+	/* inc_use */ &w83781d_inc_use,
+	/* dec_use */ &w83781d_dec_use
+};
+
+/* Used by w83781d_init/cleanup */
+static int __initdata w83781d_initialized = 0;
+
+/* 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. */
+
+/* just a guess - no datasheet */
+static ctl_table as99127f_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{0}
+};
+
+static ctl_table w83781d_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_beep},
+#ifdef W83781D_RT
+	{W83781D_SYSCTL_RT1, "rt1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_rt},
+	{W83781D_SYSCTL_RT2, "rt2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_rt},
+	{W83781D_SYSCTL_RT3, "rt3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_rt},
+#endif
+	{0}
+};
+
+/* without pwm3-4 */
+static ctl_table w83782d_isa_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+/* with pwm3-4 */
+static ctl_table w83782d_i2c_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+static ctl_table w83783s_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	/* no in1 to maintain compatibility with 781d and 782d. */
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+
+/* This function is called when:
+     * w83781d_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and w83781d_driver is still present) */
+int w83781d_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, w83781d_detect);
+}
+
+int w83781d_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i, val1, val2;
+	struct i2c_client *new_client;
+	struct w83781d_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa = i2c_is_isa_adapter(adapter);
+	enum vendor { winbond, asus } vendid;
+
+	if (!is_isa
+	    && !i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) goto
+		    ERROR0;
+
+	if (is_isa) {
+		if (check_region(address, W83781D_EXTENT))
+			goto ERROR0;
+	}
+
+	/* Probe whether there is anything available on this address. Already
+	   done for SMBus clients */
+	if (kind < 0) {
+		if (is_isa) {
+
+#define REALLY_SLOW_IO
+			/* We need the timeouts for at least some LM78-like chips. But only
+			   if we read 'undefined' registers. */
+			i = inb_p(address + 1);
+			if (inb_p(address + 2) != i)
+				goto ERROR0;
+			if (inb_p(address + 3) != i)
+				goto ERROR0;
+			if (inb_p(address + 7) != i)
+				goto ERROR0;
+#undef REALLY_SLOW_IO
+
+			/* Let's just hope nothing breaks here */
+			i = inb_p(address + 5) & 0x7f;
+			outb_p(~i & 0x7f, address + 5);
+			if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+				outb_p(i, address + 5);
+				return 0;
+			}
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access w83781d_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct w83781d_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct w83781d_data *) (new_client + 1);
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83781d_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	/* The w8378?d may be stuck in some other bank than bank 0. This may
+	   make reading other information impossible. Specify a force=... or
+	   force_*=... parameter, and the Winbond will be reset to the right
+	   bank. */
+	if (kind < 0) {
+		if (w83781d_read_value(new_client, W83781D_REG_CONFIG) &
+		    0x80) goto ERROR1;
+		val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
+		val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+		/* Check for Winbond or Asus ID if in bank 0 */
+		if ((!(val1 & 0x07)) &&
+		    (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
+		     || ((val1 & 0x80) && (val2 != 0x5c)
+			 && (val2 != 0x12)))) goto ERROR1;
+		/* If Winbond SMBus, check address at 0x48. Asus doesn't support */
+		if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
+				  ((val1 & 0x80) && (val2 == 0x5c)))) {
+			if (w83781d_read_value
+			    (new_client, W83781D_REG_I2C_ADDR) != address)
+				goto ERROR1;
+		}
+	}
+
+	/* We have either had a force parameter, or we have already detected the
+	   Winbond. Put it now into bank 0 and Vendor ID High Byte */
+	w83781d_write_value(new_client, W83781D_REG_BANK,
+			    (w83781d_read_value(new_client,
+						W83781D_REG_BANK) & 0x78) |
+			    0x80);
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		/* get vendor ID */
+		val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+		if (val2 == 0x5c)
+			vendid = winbond;
+		else if (val2 == 0x12)
+			vendid = asus;
+		else
+			goto ERROR1;
+		/* mask off lower bit, not reliable */
+		val1 =
+		    w83781d_read_value(new_client,
+				       W83781D_REG_WCHIPID) & 0xfe;
+		if (val1 == 0x10 && vendid == winbond)
+			kind = w83781d;
+		else if (val1 == 0x30 && vendid == winbond)
+			kind = w83782d;
+		else if (val1 == 0x40 && vendid == winbond && !is_isa)
+			kind = w83783s;
+		else if (val1 == 0x20 && vendid == winbond)
+			kind = w83627hf;
+		else if (val1 == 0x30 && vendid == asus && !is_isa)
+			kind = as99127f;
+		else {
+			if (kind == 0)
+				printk
+				    ("w83781d.o: Ignoring 'force' parameter for unknown chip at"
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == w83781d) {
+		type_name = "w83781d";
+		client_name = "W83781D chip";
+	} else if (kind == w83782d) {
+		type_name = "w83782d";
+		client_name = "W83782D chip";
+	} else if (kind == w83783s) {
+		type_name = "w83783s";
+		client_name = "W83783S chip";
+	} else if (kind == w83627hf) {
+		type_name = "w83627hf";
+		client_name = "W83627HF chip";
+	} else if (kind == as99127f) {
+		type_name = "as99127f";
+		client_name = "AS99127F chip";
+	} else {
+#ifdef DEBUG
+		printk("w83781d.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	if (is_isa)
+		request_region(address, W83781D_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = w83781d_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* attach secondary i2c lm75-like clients */
+	if (!is_isa) {
+		if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client),
+					   GFP_KERNEL))) {
+			err = -ENOMEM;
+			goto ERROR4;
+		}
+		val1 =
+		    w83781d_read_value(new_client,
+				       W83781D_REG_I2C_SUBADDR);
+		data->lm75[0].addr = 0x48 + (val1 & 0x07);
+		data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07);
+		if (kind == w83781d)
+			client_name = "W83781D subclient";
+		else if (kind == w83782d)
+			client_name = "W83782D subclient";
+		else if (kind == w83783s)
+			client_name = "W83783S subclient";
+		else if (kind == w83627hf)
+			client_name = "W83627HF subclient";
+		else if (kind == as99127f)
+			client_name = "AS99127F subclient";
+
+		for (i = 0; i <= 1; i++) {
+			data->lm75[i].data = NULL;	/* store all data in w83781d */
+			data->lm75[i].adapter = adapter;
+			data->lm75[i].driver = &w83781d_driver;
+			data->lm75[i].flags = 0;
+			strcpy(data->lm75[i].name, client_name);
+			data->lm75[i].id = w83781d_id++;
+			if ((err = i2c_attach_client(&(data->lm75[i])))) {
+				if (i == 1)
+					goto ERROR6;
+				else
+					goto ERROR5;
+			}
+			if (kind == w83783s)
+				break;
+		}
+	} else {
+		data->lm75 = NULL;
+	}
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					kind == as99127f ?
+					   as99127f_dir_table_template :
+					kind == w83781d ?
+					   w83781d_dir_table_template :
+					kind == w83783s ?
+					   w83783s_dir_table_template :
+					(is_isa || kind == w83627hf) ?
+					   w83782d_isa_dir_table_template :
+					   w83782d_i2c_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR7;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the Winbond chip */
+	w83781d_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR7:
+	if (!is_isa)
+		i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (new_client->data))->
+				   lm75[1]));
+      ERROR6:
+	if (!is_isa)
+		i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (new_client->data))->
+				   lm75[0]));
+      ERROR5:
+	if (!is_isa)
+		kfree(((struct w83781d_data *) (new_client->data))->lm75);
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	if (is_isa)
+		release_region(address, W83781D_EXTENT);
+      ERROR1:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int w83781d_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct w83781d_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("w83781d.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	if i2c_is_isa_client
+		(client)
+		    release_region(client->addr, W83781D_EXTENT);
+	else {
+		i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (client->data))->
+				   lm75[0]));
+		if((((struct w83781d_data *) (client->data))->type) != w83783s)
+			i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (client->data))->
+				   lm75[1]));
+		kfree(((struct w83781d_data *) (client->data))->lm75);
+	}
+	kfree(client);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int w83781d_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void w83781d_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void w83781d_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* The SMBus locks itself, usually, but nothing may access the Winbond between
+   bank switches. ISA access must always be locked explicitly! 
+   We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the W83781D access and should not be necessary. 
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+int w83781d_read_value(struct i2c_client *client, u16 reg)
+{
+	int res, word_sized, bank;
+	struct i2c_client *cl;
+
+	down(&(((struct w83781d_data *) (client->data))->lock));
+	if (i2c_is_isa_client(client)) {
+		word_sized = (((reg & 0xff00) == 0x100)
+			      || ((reg & 0xff00) == 0x200))
+		    && (((reg & 0x00ff) == 0x50)
+			|| ((reg & 0x00ff) == 0x53)
+			|| ((reg & 0x00ff) == 0x55));
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(reg >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+		}
+		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+		res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
+		if (word_sized) {
+			outb_p((reg & 0xff) + 1,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			res =
+			    (res << 8) + inb_p(client->addr +
+					       W83781D_DATA_REG_OFFSET);
+		}
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+		}
+	} else {
+		bank = (reg >> 8) & 0x0f;
+		if (bank > 2)
+			/* switch banks */
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  bank);
+		if (bank == 0 || bank > 2) {
+			res = i2c_smbus_read_byte_data(client, reg & 0xff);
+		} else {
+			/* switch to subclient */
+			cl =
+			    &(((struct w83781d_data *) (client->data))->
+			      lm75[bank - 1]);
+			/* convert from ISA to LM75 I2C addresses */
+			switch (reg & 0xff) {
+			case 0x50: /* TEMP */
+				res =
+				    swap_bytes(i2c_smbus_read_word_data
+					       (cl, 0));
+				break;
+			case 0x52: /* CONFIG */
+				res = i2c_smbus_read_byte_data(cl, 1);
+				break;
+			case 0x53: /* HYST */
+				res =
+				    swap_bytes(i2c_smbus_read_word_data
+					       (cl, 2));
+				break;
+			case 0x55: /* OVER */
+			default:
+				res =
+				    swap_bytes(i2c_smbus_read_word_data
+					       (cl, 3));
+				break;
+			}
+		}
+		if (bank > 2)
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  0);
+	}
+	up(&(((struct w83781d_data *) (client->data))->lock));
+	return res;
+}
+
+int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+	int word_sized, bank;
+	struct i2c_client *cl;
+
+	down(&(((struct w83781d_data *) (client->data))->lock));
+	if (i2c_is_isa_client(client)) {
+		word_sized = (((reg & 0xff00) == 0x100)
+			      || ((reg & 0xff00) == 0x200))
+		    && (((reg & 0x00ff) == 0x53)
+			|| ((reg & 0x00ff) == 0x55));
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(reg >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+		}
+		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+		if (word_sized) {
+			outb_p(value >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+			outb_p((reg & 0xff) + 1,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+		}
+		outb_p(value & 0xff,
+		       client->addr + W83781D_DATA_REG_OFFSET);
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+		}
+	} else {
+		bank = (reg >> 8) & 0x0f;
+		if (bank > 2)
+			/* switch banks */
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  bank);
+		if (bank == 0 || bank > 2) {
+			i2c_smbus_write_byte_data(client, reg & 0xff,
+						  value & 0xff);
+		} else {
+			/* switch to subclient */
+			cl = &(((struct w83781d_data *) (client->data))->
+			      lm75[bank - 1]);
+			/* convert from ISA to LM75 I2C addresses */
+			switch (reg & 0xff) {
+			case 0x52: /* CONFIG */
+				i2c_smbus_write_byte_data(cl, 1,
+							  value & 0xff);
+				break;
+			case 0x53: /* HYST */
+				i2c_smbus_write_word_data(cl, 2,
+							  swap_bytes(value));
+				break;
+			case 0x55: /* OVER */
+				i2c_smbus_write_word_data(cl, 3,
+							  swap_bytes(value));
+				break;
+			}
+		}
+		if (bank > 2)
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  0);
+	}
+	up(&(((struct w83781d_data *) (client->data))->lock));
+	return 0;
+}
+
+/* Called when we have found a new W83781D. It should set limits, etc. */
+void w83781d_init_client(struct i2c_client *client)
+{
+	struct w83781d_data *data = client->data;
+	int vid, i;
+	int type = data->type;
+	u8 tmp;
+
+	/* save this register */
+	i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others */
+	w83781d_write_value(client, W83781D_REG_CONFIG, 0x80);
+	/* Restore the register and disable power-on abnormal beep.
+	   This saves FAN 1/2/3 input/output values set by BIOS. */
+	w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80);
+	/* Disable master beep-enable (reset turns it on).
+	   Individual beeps should be reset to off but for some reason
+	   disabling this bit helps some people not get beeped */
+	w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0);
+
+	vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f;
+	vid |=
+	    (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) << 4;
+	vid = VID_FROM_REG(vid);
+
+	if ((type != w83781d) && (type != as99127f)) {
+		tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+		for (i = 1; i <= 3; i++) {
+			if (!(tmp & BIT_SCFG1[i - 1])) {
+				data->sens[i - 1] = W83781D_DEFAULT_BETA;
+			} else {
+				if (w83781d_read_value
+				    (client,
+				     W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
+					data->sens[i - 1] = 1;
+				else
+					data->sens[i - 1] = 2;
+			}
+			if ((type == w83783s) && (i == 2))
+				break;
+		}
+	}
+#ifdef W83781D_RT
+/*
+   Fill up the RT Tables.
+   We assume that they are 32 bytes long, in order for temp 1-3.
+   Data sheet documentation is sparse.
+   We also assume that it is only for the 781D although I suspect
+   that the others support it as well....
+*/
+
+	if (type == w83781d) {
+		u16 k = 0;
+/*
+    Auto-indexing doesn't seem to work...
+    w83781d_write_value(client,W83781D_REG_RT_IDX,0);
+*/
+		for (i = 0; i < 3; i++) {
+			int j;
+			for (j = 0; j < 32; j++) {
+				w83781d_write_value(client,
+						    W83781D_REG_RT_IDX,
+						    k++);
+				data->rt[i][j] =
+				    w83781d_read_value(client,
+						       W83781D_REG_RT_VAL);
+			}
+		}
+	}
+#endif				/* W83781D_RT */
+
+	w83781d_write_value(client, W83781D_REG_IN_MIN(0),
+			    IN_TO_REG(W83781D_INIT_IN_MIN_0));
+	w83781d_write_value(client, W83781D_REG_IN_MAX(0),
+			    IN_TO_REG(W83781D_INIT_IN_MAX_0));
+	if (type != w83783s) {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(1),
+				    IN_TO_REG(W83781D_INIT_IN_MIN_1));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(1),
+				    IN_TO_REG(W83781D_INIT_IN_MAX_1));
+	}
+
+	w83781d_write_value(client, W83781D_REG_IN_MIN(2),
+			    IN_TO_REG(W83781D_INIT_IN_MIN_2));
+	w83781d_write_value(client, W83781D_REG_IN_MAX(2),
+			    IN_TO_REG(W83781D_INIT_IN_MAX_2));
+	w83781d_write_value(client, W83781D_REG_IN_MIN(3),
+			    IN_TO_REG(W83781D_INIT_IN_MIN_3));
+	w83781d_write_value(client, W83781D_REG_IN_MAX(3),
+			    IN_TO_REG(W83781D_INIT_IN_MAX_3));
+	w83781d_write_value(client, W83781D_REG_IN_MIN(4),
+			    IN_TO_REG(W83781D_INIT_IN_MIN_4));
+	w83781d_write_value(client, W83781D_REG_IN_MAX(4),
+			    IN_TO_REG(W83781D_INIT_IN_MAX_4));
+	if (type == w83781d || type == as99127f) {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(5),
+				    IN_TO_REG(W83781D_INIT_IN_MIN_5));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(5),
+				    IN_TO_REG(W83781D_INIT_IN_MAX_5));
+	} else {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(5),
+				    IN_TO_REG(W83782D_INIT_IN_MIN_5));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(5),
+				    IN_TO_REG(W83782D_INIT_IN_MAX_5));
+	}
+	if (type == w83781d || type == as99127f) {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(6),
+				    IN_TO_REG(W83781D_INIT_IN_MIN_6));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(6),
+				    IN_TO_REG(W83781D_INIT_IN_MAX_6));
+	} else {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(6),
+				    IN_TO_REG(W83782D_INIT_IN_MIN_6));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(6),
+				    IN_TO_REG(W83782D_INIT_IN_MAX_6));
+	}
+	if ((type == w83782d) || (type == w83627hf)) {
+		w83781d_write_value(client, W83781D_REG_IN_MIN(7),
+				    IN_TO_REG(W83781D_INIT_IN_MIN_7));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(7),
+				    IN_TO_REG(W83781D_INIT_IN_MAX_7));
+		w83781d_write_value(client, W83781D_REG_IN_MIN(8),
+				    IN_TO_REG(W83781D_INIT_IN_MIN_8));
+		w83781d_write_value(client, W83781D_REG_IN_MAX(8),
+				    IN_TO_REG(W83781D_INIT_IN_MAX_8));
+		w83781d_write_value(client, W83781D_REG_VBAT,
+		    (w83781d_read_value(client, W83781D_REG_VBAT) | 0x01));
+	}
+	if (type == as99127f) {
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(1),
+				    AS_FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2));
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(2),
+				    AS_FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2));
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(3),
+				    AS_FAN_TO_REG(W83781D_INIT_FAN_MIN_3, 2));
+	} else {
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(1),
+				    FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2));
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(2),
+				    FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2));
+		w83781d_write_value(client, W83781D_REG_FAN_MIN(3),
+				    FAN_TO_REG(W83781D_INIT_FAN_MIN_3, 2));
+	}
+
+	w83781d_write_value(client, W83781D_REG_TEMP_OVER,
+			    TEMP_TO_REG(W83781D_INIT_TEMP_OVER));
+	w83781d_write_value(client, W83781D_REG_TEMP_HYST,
+			    TEMP_TO_REG(W83781D_INIT_TEMP_HYST));
+
+	if (type == as99127f) {
+		w83781d_write_value(client, W83781D_REG_TEMP2_OVER,
+				    AS99127_TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP2_OVER));
+		w83781d_write_value(client, W83781D_REG_TEMP2_HYST,
+				    AS99127_TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP2_HYST));
+	} else {
+		w83781d_write_value(client, W83781D_REG_TEMP2_OVER,
+				    TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP2_OVER));
+		w83781d_write_value(client, W83781D_REG_TEMP2_HYST,
+				    TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP2_HYST));
+	}
+	w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00);
+
+	if (type == as99127f) {
+		w83781d_write_value(client, W83781D_REG_TEMP3_OVER,
+				    AS99127_TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP3_OVER));
+		w83781d_write_value(client, W83781D_REG_TEMP3_HYST,
+				    AS99127_TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP3_HYST));
+	} else if (type != w83783s) {
+		w83781d_write_value(client, W83781D_REG_TEMP3_OVER,
+				    TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP3_OVER));
+		w83781d_write_value(client, W83781D_REG_TEMP3_HYST,
+				    TEMP_ADD_TO_REG
+				    (W83781D_INIT_TEMP3_HYST));
+	}
+	if (type != w83783s) {
+		w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG,
+				    0x00);
+	}
+	/* enable PWM2 control (can't hurt since PWM reg should have been
+           reset to 0xff) */
+	if (type != w83781d) {
+		w83781d_write_value(client, W83781D_REG_PWMCLK12, 0x19);
+	}
+
+	/* Start monitoring */
+	w83781d_write_value(client, W83781D_REG_CONFIG,
+			    (w83781d_read_value(client,
+						W83781D_REG_CONFIG) & 0xf7)
+			    | 0x01);
+}
+
+void w83781d_update_client(struct i2c_client *client)
+{
+	struct w83781d_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting w83781d update\n");
+#endif
+		for (i = 0; i <= 8; i++) {
+			if ((data->type == w83783s) && (i == 1))
+				continue;	/* 783S has no in1 */
+			data->in[i] =
+			    w83781d_read_value(client, W83781D_REG_IN(i));
+			data->in_min[i] =
+			    w83781d_read_value(client,
+					       W83781D_REG_IN_MIN(i));
+			data->in_max[i] =
+			    w83781d_read_value(client,
+					       W83781D_REG_IN_MAX(i));
+			if ((data->type != w83782d)
+			    && (data->type != w83627hf) && (i == 6))
+				break;
+		}
+		for (i = 1; i <= 3; i++) {
+			data->fan[i - 1] =
+			    w83781d_read_value(client, W83781D_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    w83781d_read_value(client,
+					       W83781D_REG_FAN_MIN(i));
+		}
+		if (data->type != w83781d) {
+			for (i = 1; i <= 4; i++) {
+				data->pwm[i - 1] =
+				    w83781d_read_value(client,
+						       W83781D_REG_PWM(i));
+				if (((data->type == w83783s)
+				     || (data->type == w83627hf)
+				     || (data->type == as99127f)
+				     || ((data->type == w83782d)
+				        && i2c_is_isa_client(client)))
+				    && i == 2)
+					break;
+			}
+		}
+
+		data->temp = w83781d_read_value(client, W83781D_REG_TEMP);
+		data->temp_over =
+		    w83781d_read_value(client, W83781D_REG_TEMP_OVER);
+		data->temp_hyst =
+		    w83781d_read_value(client, W83781D_REG_TEMP_HYST);
+		data->temp_add[0] =
+		    w83781d_read_value(client, W83781D_REG_TEMP2);
+		data->temp_add_over[0] =
+		    w83781d_read_value(client, W83781D_REG_TEMP2_OVER);
+		data->temp_add_hyst[0] =
+		    w83781d_read_value(client, W83781D_REG_TEMP2_HYST);
+		if (data->type != w83783s) {
+			data->temp_add[1] =
+			    w83781d_read_value(client, W83781D_REG_TEMP3);
+			data->temp_add_over[1] =
+			    w83781d_read_value(client, W83781D_REG_TEMP3_OVER);
+			data->temp_add_hyst[1] =
+			    w83781d_read_value(client, W83781D_REG_TEMP3_HYST);
+		}
+		i = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01)
+		    << 4;
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		data->fan_div[2] = (w83781d_read_value(client,
+						       W83781D_REG_PIN) >>
+				    6) & 0x03;
+		if ((data->type != w83781d) && (data->type != as99127f)) {
+			i = w83781d_read_value(client, W83781D_REG_VBAT);
+			data->fan_div[0] |= (i >> 3) & 0x04;
+			data->fan_div[1] |= (i >> 4) & 0x04;
+			data->fan_div[2] |= (i >> 5) & 0x04;
+		}
+		data->alarms =
+		    w83781d_read_value(client,
+				       W83781D_REG_ALARM1) +
+		    (w83781d_read_value(client, W83781D_REG_ALARM2) << 8);
+		if ((data->type == w83782d) || (data->type == w83627hf)) {
+			data->alarms |=
+			    w83781d_read_value(client,
+					       W83781D_REG_ALARM3) << 16;
+		}
+		i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2);
+		data->beep_enable = i >> 7;
+		data->beeps = ((i & 0x7f) << 8) +
+		    w83781d_read_value(client, W83781D_REG_BEEP_INTS1);
+		if ((data->type != w83781d) && (data->type != as99127f)) {
+			data->beeps |=
+			    w83781d_read_value(client,
+					       W83781D_REG_BEEP_INTS3) << 16;
+		}
+		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 w83781d_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_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]);
+			w83781d_write_value(client, W83781D_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			w83781d_write_value(client, W83781D_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void w83781d_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		if(data->type == as99127f) {
+			results[0] = AS_FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]));
+			results[1] = AS_FAN_FROM_REG(data->fan[nr - 1],
+				          DIV_FROM_REG(data->fan_div[nr - 1]));
+		} else {
+			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) {
+			if(data->type == as99127f) {
+				data->fan_min[nr - 1] =
+				     AS_FAN_TO_REG(results[0],
+				            DIV_FROM_REG(data->fan_div[nr-1]));
+			} else {
+				data->fan_min[nr - 1] =
+				     FAN_TO_REG(results[0],
+				            DIV_FROM_REG(data->fan_div[nr-1]));
+			}
+			w83781d_write_value(client,
+					    W83781D_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void w83781d_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_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);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_TEMP_OVER,
+					    data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			w83781d_write_value(client, W83781D_REG_TEMP_HYST,
+					    data->temp_hyst);
+		}
+	}
+}
+
+void w83781d_temp_add(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_TEMP2;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		if (data->type == as99127f) {
+			results[0] =
+			    AS99127_TEMP_ADD_FROM_REG(data->
+						      temp_add_over[nr]);
+			results[1] =
+			    AS99127_TEMP_ADD_FROM_REG(data->
+						      temp_add_hyst[nr]);
+			results[2] =
+			    AS99127_TEMP_ADD_FROM_REG(data->temp_add[nr]);
+		} else {
+			results[0] =
+			    TEMP_ADD_FROM_REG(data->temp_add_over[nr]);
+			results[1] =
+			    TEMP_ADD_FROM_REG(data->temp_add_hyst[nr]);
+			results[2] = TEMP_ADD_FROM_REG(data->temp_add[nr]);
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (data->type == as99127f)
+				data->temp_add_over[nr] =
+				    AS99127_TEMP_ADD_TO_REG(results[0]);
+			else
+				data->temp_add_over[nr] =
+				    TEMP_ADD_TO_REG(results[0]);
+			w83781d_write_value(client,
+					    nr ? W83781D_REG_TEMP3_OVER :
+					    W83781D_REG_TEMP2_OVER,
+					    data->temp_add_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			if (data->type == as99127f)
+				data->temp_add_hyst[nr] =
+				    AS99127_TEMP_ADD_TO_REG(results[1]);
+			else
+				data->temp_add_hyst[nr] =
+				    TEMP_ADD_TO_REG(results[1]);
+			w83781d_write_value(client,
+					    nr ? W83781D_REG_TEMP3_HYST :
+					    W83781D_REG_TEMP2_HYST,
+					    data->temp_add_hyst[nr]);
+		}
+	}
+}
+
+
+void w83781d_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void w83781d_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void w83781d_beep(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int val;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 2) {
+			data->beeps = BEEPS_TO_REG(results[1]);
+			w83781d_write_value(client, W83781D_REG_BEEP_INTS1,
+					    data->beeps & 0xff);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				w83781d_write_value(client,
+						    W83781D_REG_BEEP_INTS3,
+						    ((data-> beeps) >> 16) &
+						      0xff);
+			}
+			val = (data->beeps >> 8) & 0x7f;
+		} else if (*nrels_mag >= 1)
+			val =
+			    w83781d_read_value(client,
+					       W83781D_REG_BEEP_INTS2) &
+			    0x7f;
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_BEEP_INTS2,
+					    val | data->beep_enable << 7);
+		}
+	}
+}
+
+void w83781d_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int old, old2, old3 = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_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;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+		/* w83781d and as99127f don't have extended divisor bits */
+		if ((data->type != w83781d) && data->type != as99127f) {
+			old3 =
+			    w83781d_read_value(client, W83781D_REG_VBAT);
+		}
+		if (*nrels_mag >= 3) {
+			data->fan_div[2] =
+			    DIV_TO_REG(results[2], data->type);
+			old2 = w83781d_read_value(client, W83781D_REG_PIN);
+			old2 =
+			    (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
+			w83781d_write_value(client, W83781D_REG_PIN, old2);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0x7f) |
+				    ((data->fan_div[2] & 0x04) << 5);
+			}
+		}
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] =
+			    DIV_TO_REG(results[1], data->type);
+			old =
+			    (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0xbf) |
+				    ((data->fan_div[1] & 0x04) << 4);
+			}
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] =
+			    DIV_TO_REG(results[0], data->type);
+			old =
+			    (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
+			w83781d_write_value(client, W83781D_REG_VID_FANDIV,
+					    old);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0xdf) |
+				    ((data->fan_div[0] & 0x04) << 3);
+				w83781d_write_value(client,
+						    W83781D_REG_VBAT,
+						    old3);
+			}
+		}
+	}
+}
+
+void w83781d_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_PWM(nr),
+					    data->pwm[nr - 1]);
+		}
+	}
+}
+
+void w83781d_sens(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1;
+	u8 tmp;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->sens[nr - 1];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			switch (results[0]) {
+			case 1:	/* PII/Celeron diode */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp | BIT_SCFG2[nr -
+								    1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case 2:	/* 3904 */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp & ~BIT_SCFG2[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case W83781D_DEFAULT_BETA:	/* thermistor */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp & ~BIT_SCFG1[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			default:
+				printk
+				    ("w83781d.o: Invalid sensor type %ld; must be 1, 2, or %d\n",
+				     results[0], W83781D_DEFAULT_BETA);
+				break;
+			}
+		}
+	}
+}
+
+#ifdef W83781D_RT
+void w83781d_rt(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_RT1;
+	int i;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		for (i = 0; i < 32; i++) {
+			results[i] = data->rt[nr - 1][i];
+		}
+		*nrels_mag = 32;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag > 32)
+			*nrels_mag = 32;
+		for (i = 0; i < *nrels_mag; i++) {
+			/* fixme: no bounds checking 0-255 */
+			data->rt[nr - 1][i] = results[i];
+			w83781d_write_value(client, W83781D_REG_RT_IDX, i);
+			w83781d_write_value(client, W83781D_REG_RT_VAL,
+					    data->rt[nr - 1][i]);
+		}
+	}
+}
+#endif
+
+int __init sensors_w83781d_init(void)
+{
+	int res;
+
+	printk("w83781d.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	w83781d_initialized = 0;
+
+	if ((res = i2c_add_driver(&w83781d_driver))) {
+		printk
+		    ("w83781d.o: Driver registration failed, module not inserted.\n");
+		w83781d_cleanup();
+		return res;
+	}
+	w83781d_initialized++;
+	return 0;
+}
+
+int __init w83781d_cleanup(void)
+{
+	int res;
+
+	if (w83781d_initialized >= 1) {
+		if ((res = i2c_del_driver(&w83781d_driver))) {
+			printk
+			    ("w83781d.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		w83781d_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83781D driver");
+
+int init_module(void)
+{
+	return sensors_w83781d_init();
+}
+
+int cleanup_module(void)
+{
+	return w83781d_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/icspll.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/icspll.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/icspll.c	(revision 933)
@@ -0,0 +1,371 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+
+/* Addresses to scan */
+#define ADDRESS 0x69
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = {ADDRESS, ADDRESS, 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 {
+	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;
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_icspll_init(void);
+static int __init icspll_cleanup(void);
+
+static int icspll_attach_adapter(struct i2c_adapter *adapter);
+static int icspll_detach_client(struct i2c_client *client);
+static int icspll_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+static int icspll_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static void icspll_inc_use(struct i2c_client *client);
+static void icspll_dec_use(struct i2c_client *client);
+
+#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,
+	/* command */ &icspll_command,
+	/* inc_use */ &icspll_inc_use,
+	/* dec_use */ &icspll_dec_use
+};
+
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &icspll_contents},
+	{0}
+};
+
+/* holding place for data - block read could be as much as 32 */
+static u8 tempdata[MAXBLOCK_SIZE];
+
+/* Used by init/cleanup */
+static int __initdata icspll_initialized = 0;
+
+static int icspll_id = 0;
+int icspll_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, icspll_detect);
+}
+
+/* This function is called by sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct icspll_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	/* Fill the new client structure with data */
+	data = (struct icspll_data *) (new_client + 1);
+	new_client->data = data;
+	new_client->id = icspll_id++;
+	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 = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int icspll_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+	return 0;
+}
+
+
+/* No commands defined yet */
+int icspll_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void icspll_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void icspll_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+#if 0
+/* No writes yet (PAE) */
+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
+
+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");
+	}
+}
+
+int __init sensors_icspll_init(void)
+{
+	int res;
+
+	printk("icspll.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	icspll_initialized = 0;
+	if ((res = i2c_add_driver(&icspll_driver))) {
+		printk
+		    ("icspll.o: Driver registration failed, module not inserted.\n");
+		icspll_cleanup();
+		return res;
+	}
+	icspll_initialized++;
+	return 0;
+}
+
+int __init icspll_cleanup(void)
+{
+	int res;
+
+	if (icspll_initialized >= 1) {
+		if ((res = i2c_del_driver(&icspll_driver))) {
+			printk
+			    ("icspll.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+	} else
+		icspll_initialized--;
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("ICSPLL driver");
+
+int init_module(void)
+{
+	return sensors_icspll_init();
+}
+
+int cleanup_module(void)
+{
+	return icspll_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/sis5595.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/sis5595.c	(revision 943)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/sis5595.c	(revision 943)
@@ -0,0 +1,789 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                        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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/pci.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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);
+
+/*
+   SiS southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+*/
+
+/* Many SIS5595 constants specified below */
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+#define SIS5595_BASE_REG 0x68
+#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))
+
+#define SIS5595_REG_TEMP 0x27
+#define SIS5595_REG_TEMP_OVER 0x39
+#define SIS5595_REG_TEMP_HYST 0x3a
+
+#define SIS5595_REG_ALARM1 0x41
+
+#define SIS5595_REG_FANDIV 0x47
+
+#define SIS5595_REG_CONFIG 0x40
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16) / 10)
+
+extern inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* Initial limits. To keep them sane, we use the 'standard' translation as
+   specified in the SIS5595 sheet. Use the config file to set better limits. */
+#define SIS5595_INIT_IN_0 (((1200)  * 10)/38)
+#define SIS5595_INIT_IN_1 (((500)   * 100)/168)
+#define SIS5595_INIT_IN_2 330
+#define SIS5595_INIT_IN_3 250
+
+#define SIS5595_INIT_IN_PERCENTAGE 10
+
+#define SIS5595_INIT_IN_MIN_0 \
+        (SIS5595_INIT_IN_0 - SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MAX_0 \
+        (SIS5595_INIT_IN_0 + SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MIN_1 \
+        (SIS5595_INIT_IN_1 - SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MAX_1 \
+        (SIS5595_INIT_IN_1 + SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MIN_2 \
+        (SIS5595_INIT_IN_2 - SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MAX_2 \
+        (SIS5595_INIT_IN_2 + SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MIN_3 \
+        (SIS5595_INIT_IN_3 - SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100)
+#define SIS5595_INIT_IN_MAX_3 \
+        (SIS5595_INIT_IN_3 + SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100)
+
+#define SIS5595_INIT_FAN_MIN_1 3000
+#define SIS5595_INIT_FAN_MIN_2 3000
+
+#define SIS5595_INIT_TEMP_OVER 600
+#define SIS5595_INIT_TEMP_HYST 500
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered 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 same time when a new sis5595 client is
+   allocated. */
+struct sis5595_data {
+	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[4];		/* Register value */
+	u8 in_max[4];		/* Register value */
+	u8 in_min[4];		/* 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 */
+	u8 alarms;		/* Register encoding, combined */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_sis5595_init(void);
+static int __init sis5595_cleanup(void);
+
+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_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void sis5595_inc_use(struct i2c_client *client);
+static void sis5595_dec_use(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 int sis5595_find_sis(int *address);
+
+
+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);
+
+static int sis5595_id = 0;
+
+/* The driver. I choose to use type i2c_driver, as at is identical to both
+   smbus_driver and isa_driver, and clients could be of either kind */
+static struct i2c_driver sis5595_driver = {
+	/* name */ "SiS 5595",
+	/* id */ I2C_DRIVERID_SIS5595,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &sis5595_attach_adapter,
+	/* detach_client */ &sis5595_detach_client,
+	/* command */ &sis5595_command,
+	/* inc_use */ &sis5595_inc_use,
+	/* dec_use */ &sis5595_dec_use
+};
+
+/* Used by sis5595_init/cleanup */
+static int __initdata sis5595_initialized = 0;
+
+/* The /proc/sys entries */
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_temp},
+	{SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_fan_div},
+	{SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &sis5595_alarms},
+	{0}
+};
+
+/* This is called when the module is loaded */
+int sis5595_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, sis5595_detect);
+}
+
+/* Locate SiS bridge and correct base address for SIS5595 */
+int sis5595_find_sis(int *address)
+{
+	struct pci_dev *s_bridge;
+	u16 val;
+	char c;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	if (!(s_bridge =
+	      pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503,
+			     NULL)))
+		return -ENODEV;
+
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val))
+		return -ENODEV;
+
+	*address = (val & 0xfff8);
+	if(*address == 0) {
+		printk("sis5595.o: Sensor base address uninitialized - upgrade BIOS?\n");
+		return -ENODEV;
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &c))
+		return -ENODEV;
+	if((c & 0x80) == 0) {
+		printk("sis5595.o: Sensors not enabled - upgrade BIOS?\n");
+		return -ENODEV;
+	}
+	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 = "";
+	const char *client_name = "";
+
+	/* 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 (check_region(address, SIS5595_EXTENT))
+		goto ERROR0;
+
+	/* If this is the address as indicated by the SIS5595 chipset, we don't
+	   do any futher probing */
+	if ((kind < 0) && (address == normal_isa[0]))
+		kind = 0;
+
+	/* Probe whether there is anything available on this address. */
+	if (kind < 0) {
+#define REALLY_SLOW_IO
+		/* We need the timeouts for at least some LM78-like chips. But only
+		   if we read 'undefined' registers. */
+		i = inb_p(address + 1);
+		if (inb_p(address + 2) != i)
+			goto ERROR0;
+		if (inb_p(address + 3) != i)
+			goto ERROR0;
+		if (inb_p(address + 7) != i)
+			goto ERROR0;
+#undef REALLY_SLOW_IO
+
+		/* Let's just hope nothing breaks here */
+		i = inb_p(address + 5) & 0x7f;
+		outb_p(~i & 0x7f, address + 5);
+		if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+			outb_p(i, address + 5);
+			return 0;
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access sis5595_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct sis5595_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct sis5595_data *) (new_client + 1);
+	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;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (sis5595_read_value(new_client, SIS5595_REG_CONFIG) &
+		    0x80) goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0)
+		kind = sis5595;
+
+	if (kind == sis5595) {
+		type_name = "sis5595";
+		client_name = "SIS5595 chip";
+	} else {
+#ifdef DEBUG
+		printk("sis5595.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	request_region(address, SIS5595_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = sis5595_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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;
+
+/* 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:
+	release_region(address, SIS5595_EXTENT);
+      ERROR1:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int sis5595_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int sis5595_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void sis5595_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void sis5595_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+/* The SMBus locks itself, but ISA access must be locked explicitly! 
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+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;
+}
+
+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. It should set limits, etc. */
+void sis5595_init_client(struct i2c_client *client)
+{
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others */
+	sis5595_write_value(client, SIS5595_REG_CONFIG, 0x80);
+
+	sis5595_write_value(client, SIS5595_REG_IN_MIN(0),
+			    IN_TO_REG(SIS5595_INIT_IN_MIN_0));
+	sis5595_write_value(client, SIS5595_REG_IN_MAX(0),
+			    IN_TO_REG(SIS5595_INIT_IN_MAX_0));
+	sis5595_write_value(client, SIS5595_REG_IN_MIN(1),
+			    IN_TO_REG(SIS5595_INIT_IN_MIN_1));
+	sis5595_write_value(client, SIS5595_REG_IN_MAX(1),
+			    IN_TO_REG(SIS5595_INIT_IN_MAX_1));
+	sis5595_write_value(client, SIS5595_REG_IN_MIN(2),
+			    IN_TO_REG(SIS5595_INIT_IN_MIN_2));
+	sis5595_write_value(client, SIS5595_REG_IN_MAX(2),
+			    IN_TO_REG(SIS5595_INIT_IN_MAX_2));
+	sis5595_write_value(client, SIS5595_REG_IN_MIN(3),
+			    IN_TO_REG(SIS5595_INIT_IN_MIN_3));
+	sis5595_write_value(client, SIS5595_REG_IN_MAX(3),
+			    IN_TO_REG(SIS5595_INIT_IN_MAX_3));
+	sis5595_write_value(client, SIS5595_REG_FAN_MIN(1),
+			    FAN_TO_REG(SIS5595_INIT_FAN_MIN_1, 2));
+	sis5595_write_value(client, SIS5595_REG_FAN_MIN(2),
+			    FAN_TO_REG(SIS5595_INIT_FAN_MIN_2, 2));
+	sis5595_write_value(client, SIS5595_REG_TEMP_OVER,
+			    TEMP_TO_REG(SIS5595_INIT_TEMP_OVER));
+	sis5595_write_value(client, SIS5595_REG_TEMP_HYST,
+			    TEMP_TO_REG(SIS5595_INIT_TEMP_HYST));
+
+	/* Start monitoring */
+	sis5595_write_value(client, SIS5595_REG_CONFIG,
+			    (sis5595_read_value(client, SIS5595_REG_CONFIG)
+			     & 0xf7) | 0x01);
+
+}
+
+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) {
+
+#ifdef DEBUG
+		printk("Starting sis5595 update\n");
+#endif
+		for (i = 0; i <= 3; 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));
+		}
+		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);
+		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 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) {
+		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]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		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]);
+		}
+	}
+}
+
+
+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) {
+		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);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		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] = ALARMS_FROM_REG(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);
+		}
+	}
+}
+
+int __init sensors_sis5595_init(void)
+{
+	int res, addr;
+
+	printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	sis5595_initialized = 0;
+
+	if (sis5595_find_sis(&addr)) {
+		printk("sis5595.o: SIS5595 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	if ((res = i2c_add_driver(&sis5595_driver))) {
+		printk
+		    ("sis5595.o: Driver registration failed, module not inserted.\n");
+		sis5595_cleanup();
+		return res;
+	}
+	sis5595_initialized++;
+	return 0;
+}
+
+int __init sis5595_cleanup(void)
+{
+	int res;
+
+	if (sis5595_initialized >= 1) {
+		if ((res = i2c_del_driver(&sis5595_driver))) {
+			printk
+			    ("sis5595.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		sis5595_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("SiS 5595 Sensor device");
+
+int init_module(void)
+{
+	return sensors_sis5595_init();
+}
+
+int cleanup_module(void)
+{
+	return sis5595_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/lm87.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/lm87.c	(revision 980)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/lm87.c	(revision 980)
@@ -0,0 +1,1083 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.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.
+   Ironicly, 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. ;') */
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* 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. */
+
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+extern 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(temp)  (temp * 10)
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10),0,255)
+#if 0
+#define TEMP_FROM_REG(temp) \
+   ((temp)<256?((((temp)&0x1fe) >> 1) * 10)      + ((temp) & 1) * 5:  \
+               ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5)  \
+
+#define TEMP_LIMIT_FROM_REG(val) (val)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val),0,255)
+#endif
+
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+                           
+#define LM87_INIT_FAN_MIN 3000
+
+#define LM87_INIT_EXT_TEMP_MAX 600
+#define LM87_INIT_EXT_TEMP_MIN 100
+#define LM87_INIT_INT_TEMP_MAX 600
+#define LM87_INIT_INT_TEMP_MIN 100
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* For each registered LM87, we need to keep some data in memory. That
+   data is pointed to by LM87_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new LM87 client is
+   allocated. */
+struct LM87_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  in[6];		/* Register value */
+	u8  in_max[6];		/* Register value */
+	u8  in_min[6];		/* 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 */
+	int ext2_temp;		/* Temp, shifted right */
+	int ext_temp;           /* Temp, shifted right */
+	int int_temp;		/* Temp, shifted right */
+	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 */
+};
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_LM87_init(void);
+static int __init LM87_cleanup(void);
+
+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_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void LM87_inc_use(struct i2c_client *client);
+static void LM87_dec_use(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);
+static void LM87_ain(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+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);
+
+/* I choose here for semi-static LM87 allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int LM87_id = 0;
+
+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,
+	/* command */        &LM87_command,
+	/* inc_use */        &LM87_inc_use,
+	/* dec_use */        &LM87_dec_use
+};
+
+/* Used by LM87_init/cleanup */
+static int __initdata LM87_initialized = 0;
+
+/* 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, "ain1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_ain},
+#endif
+#ifdef LM87_AIN2
+	{LM87_SYSCTL_AIN2, "ain2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_ain},
+#endif
+#ifndef LM87_EXT2
+	{LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+	{LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+#endif
+	{LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+	{LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+	{LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+	{LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_in},
+#ifndef LM87_AIN1
+	{LM87_SYSCTL_FAN1, "fan", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_fan},
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_fan_div},
+#define LM87_FANDIV_FLAG
+#endif
+#ifndef LM87_AIN2
+	{LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_fan},
+#ifndef LM87_FANDIV_FLAG
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_fan_div},
+#endif /* LM87_FANDIV_FLAG */
+#endif /* LM87_AIN2 */
+#ifdef LM87_EXT2
+        {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_temp},
+#endif
+	{LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_temp},
+	{LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_temp},
+	{LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_alarms},
+	{LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_analog_out},
+	{LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	  &sensors_sysctl_real, NULL, &LM87_vid},
+	{0}
+};
+
+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);
+	sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct LM87_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct LM87_data *) (new_client + 1);
+	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 it into the global list */
+        type_name = "lm87";
+        client_name = "LM87 chip";
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = LM87_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int LM87_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct LM87_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm87.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+
+}
+
+/* No commands defined yet */
+int LM87_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void LM87_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void LM87_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+int LM87_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+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. It should set limits, etc. */
+void LM87_init_client(struct i2c_client *client)
+{
+	long vid;
+
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others. This makes most other
+	   initializations unnecessary */
+	LM87_write_value(client, LM87_REG_CONFIG, 0x80);
+
+        /* 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
+	 */
+
+/* I know, not clean, but it works. :'p */
+	LM87_write_value(client, LM87_REG_CHANNEL_MODE,
+#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
+	);
+
+	/* Set IN (voltage) initial limits to sane values  +/- 5% */
+	LM87_write_value(client, LM87_REG_IN_MIN(1),182);
+	LM87_write_value(client, LM87_REG_IN_MAX(1),202);
+	LM87_write_value(client, LM87_REG_IN_MIN(2),182);
+	LM87_write_value(client, LM87_REG_IN_MAX(2),202);
+	LM87_write_value(client, LM87_REG_IN_MIN(3),182);
+	LM87_write_value(client, LM87_REG_IN_MAX(3),202);
+	LM87_write_value(client, LM87_REG_IN_MIN(4),182);
+	LM87_write_value(client, LM87_REG_IN_MAX(4),202);
+
+	/* Set CPU core voltage limits relative to vid readings */
+	vid = (LM87_read_value(client, LM87_REG_VID_FAN_DIV) & 0x0f)
+		    | ((LM87_read_value(client, LM87_REG_VID4) & 0x01)
+                    << 4 );
+        if ((vid == 0x1f) || (vid == 0x0f)) {
+		vid = 0;
+        } else if (vid > 0x0f) {
+                vid = (1275 - ((vid - 0x10) * 25))/10;
+        } else {
+                vid = 200 - (vid * 5);
+        }
+	LM87_write_value(client, LM87_REG_IN_MIN(0),
+		(u8)(0x0FF & (((1920/270)*(vid*95))/1000) ));
+	LM87_write_value(client, LM87_REG_IN_MAX(0),
+		(u8)(0x0FF & (((1920/270)*(vid*105))/1000) ));
+	LM87_write_value(client, LM87_REG_IN_MIN(5),
+		(u8)(0x0FF & (((1920/270)*(vid*95))/1000) ));
+	LM87_write_value(client, LM87_REG_IN_MAX(5),
+		(u8)(0x0FF & (((1920/270)*(vid*105))/1000) ));
+
+	/* Set Temp initial limits to sane values */
+	LM87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX));
+	LM87_write_value(client, LM87_REG_EXT_TEMP_1_LOW,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN));
+#ifdef LM87_EXT2
+	LM87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX));
+	LM87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN));
+#endif
+	LM87_write_value(client, LM87_REG_INT_TEMP_HIGH,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MAX));
+	LM87_write_value(client, LM87_REG_INT_TEMP_LOW,
+			    TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MIN));
+
+#ifndef LM87_AIN1
+	LM87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT,
+			    FAN_TO_REG(LM87_INIT_FAN_MIN, 2));
+#endif
+#ifndef LM87_AIN2
+	LM87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT,
+			    FAN_TO_REG(LM87_INIT_FAN_MIN, 2));
+#endif
+
+	/* Start monitoring */
+	LM87_write_value(client, LM87_REG_CONFIG, 0x01);
+}
+
+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_FAN1_AIN1);
+		 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] = { 270, 100, 
+#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] =
+		    ((long)data->in_min[nr] * scales[nr]) / 192;
+		results[1] =
+		    ((long)data->in_max[nr] * scales[nr]) / 192;
+		results[2] =
+		    ((long)data->in[nr] * scales[nr]) / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    (results[0] * 192) / 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];
+			LM87_write_value(client, LM87_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+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);
+		 }
+		}
+	}
+}
+
+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_LIMIT_FROM_REG(data->ext2_temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->ext2_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext2_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP2)
+	   {
+		results[0] = TEMP_LIMIT_FROM_REG(data->ext_temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->ext_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP1)
+	   {
+		results[0] = TEMP_LIMIT_FROM_REG(data->int_temp_max);
+		results[1] = TEMP_LIMIT_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->int_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->int_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] = ALARMS_FROM_REG(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 = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		LM87_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+        *nrels_mag = 1;
+	}
+}
+
+int __init sensors_LM87_init(void)
+{
+	int res;
+
+	printk("lm87.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	LM87_initialized = 0;
+
+	if ((res = i2c_add_driver(&LM87_driver))) {
+		printk
+		    ("lm87.o: Driver registration failed, module not inserted.\n");
+		LM87_cleanup();
+		return res;
+	}
+	LM87_initialized++;
+	return 0;
+}
+
+int __init LM87_cleanup(void)
+{
+	int res;
+
+	if (LM87_initialized >= 1) {
+		if ((res = i2c_del_driver(&LM87_driver))) {
+			printk
+			    ("lm87.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		LM87_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+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");
+
+int init_module(void)
+{
+	return sensors_LM87_init();
+}
+
+int cleanup_module(void)
+{
+	return LM87_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/lm78.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/lm78.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/lm78.c	(revision 933)
@@ -0,0 +1,870 @@
+/*
+    lm78.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.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_3(lm78, lm78j, lm79);
+
+/* Many LM78 constants specified below */
+
+/* Length of ISA address segment */
+#define LM78_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define LM78_ADDR_REG_OFFSET 5
+#define LM78_DATA_REG_OFFSET 6
+
+/* The LM78 registers */
+#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define LM78_REG_IN(nr) (0x20 + (nr))
+
+#define LM78_REG_FAN_MIN(nr) (0x3a + (nr))
+#define LM78_REG_FAN(nr) (0x27 + (nr))
+
+#define LM78_REG_TEMP 0x27
+#define LM78_REG_TEMP_OVER 0x39
+#define LM78_REG_TEMP_HYST 0x3a
+
+#define LM78_REG_ALARM1 0x41
+#define LM78_REG_ALARM2 0x42
+
+#define LM78_REG_VID_FANDIV 0x47
+
+#define LM78_REG_CONFIG 0x40
+#define LM78_REG_CHIPID 0x49
+#define LM78_REG_I2C_ADDR 0x48
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG 
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16) / 10)
+
+extern inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+/* Initial limits. To keep them sane, we use the 'standard' translation as
+   specified in the LM78 sheet. Use the config file to set better limits. */
+#define LM78_INIT_IN_0 (vid==350?280:vid)
+#define LM78_INIT_IN_1 (vid==350?280:vid)
+#define LM78_INIT_IN_2 330
+#define LM78_INIT_IN_3 (((500)   * 100)/168)
+#define LM78_INIT_IN_4 (((1200)  * 10)/38)
+#define LM78_INIT_IN_5 (((-1200) * -604)/2100)
+#define LM78_INIT_IN_6 (((-500)  * -604)/909)
+
+#define LM78_INIT_IN_PERCENTAGE 10
+
+#define LM78_INIT_IN_MIN_0 \
+        (LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_0 \
+        (LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_1 \
+        (LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_1 \
+        (LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_2 \
+        (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_2 \
+        (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_3 \
+        (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_3 \
+        (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_4 \
+        (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_4 \
+        (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_5 \
+        (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_5 \
+        (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MIN_6 \
+        (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
+#define LM78_INIT_IN_MAX_6 \
+        (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
+
+#define LM78_INIT_FAN_MIN_1 3000
+#define LM78_INIT_FAN_MIN_2 3000
+#define LM78_INIT_FAN_MIN_3 3000
+
+#define LM78_INIT_TEMP_OVER 600
+#define LM78_INIT_TEMP_HYST 500
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/* There are some complications in a module like this. First off, LM78 chips
+   may be both present on the SMBus and the ISA bus, and we have to handle
+   those cases separately at some places. Second, there might be several
+   LM78 chips available (well, actually, that is probably never done; but
+   it is a clean illustration of how to handle a case like that). Finally,
+   a specific chip may be attached to *both* ISA and SMBus, and we would
+   not like to detect it double. Fortunately, in the case of the LM78 at
+   least, a register tells us what SMBus address we are on, so that helps
+   a bit - except if there could be more than one SMBus. Groan. No solution
+   for this yet. */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered LM78, we need to keep some data in memory. That
+   data is pointed to by lm78_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new lm78 client is
+   allocated. */
+struct lm78_data {
+	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[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp;		/* Register value */
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_lm78_init(void);
+static int __init lm78_cleanup(void);
+
+static int lm78_attach_adapter(struct i2c_adapter *adapter);
+static int lm78_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int lm78_detach_client(struct i2c_client *client);
+static int lm78_command(struct i2c_client *client, unsigned int cmd,
+			void *arg);
+static void lm78_inc_use(struct i2c_client *client);
+static void lm78_dec_use(struct i2c_client *client);
+
+static int lm78_read_value(struct i2c_client *client, u8 register);
+static int lm78_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void lm78_update_client(struct i2c_client *client);
+static void lm78_init_client(struct i2c_client *client);
+
+
+static void lm78_in(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results);
+static void lm78_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm78_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm78_vid(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm78_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm78_fan_div(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver lm78_driver = {
+	/* name */ "LM78(-J) and LM79 sensor driver",
+	/* id */ I2C_DRIVERID_LM78,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &lm78_attach_adapter,
+	/* detach_client */ &lm78_detach_client,
+	/* command */ &lm78_command,
+	/* inc_use */ &lm78_inc_use,
+	/* dec_use */ &lm78_dec_use
+};
+
+/* Used by lm78_init/cleanup */
+static int __initdata lm78_initialized = 0;
+
+static int lm78_id = 0;
+
+/* The /proc/sys entries */
+/* These files are created for each detected LM78. 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 lm78_dir_table_template[] = {
+	{LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_temp},
+	{LM78_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_vid},
+	{LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_fan_div},
+	{LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &lm78_alarms},
+	{0}
+};
+
+
+/* This function is called when:
+     * lm78_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and lm78_driver is still present) */
+int lm78_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, lm78_detect);
+}
+
+/* This function is called by sensors_detect */
+int lm78_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct lm78_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa = i2c_is_isa_adapter(adapter);
+
+	if (!is_isa
+	    && !i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) goto
+		    ERROR0;
+
+	if (is_isa) {
+		if (check_region(address, LM78_EXTENT))
+			goto ERROR0;
+	}
+
+	/* Probe whether there is anything available on this address. Already
+	   done for SMBus clients */
+	if (kind < 0) {
+		if (is_isa) {
+
+#define REALLY_SLOW_IO
+			/* We need the timeouts for at least some LM78-like chips. But only
+			   if we read 'undefined' registers. */
+			i = inb_p(address + 1);
+			if (inb_p(address + 2) != i)
+				goto ERROR0;
+			if (inb_p(address + 3) != i)
+				goto ERROR0;
+			if (inb_p(address + 7) != i)
+				goto ERROR0;
+#undef REALLY_SLOW_IO
+
+			/* Let's just hope nothing breaks here */
+			i = inb_p(address + 5) & 0x7f;
+			outb_p(~i & 0x7f, address + 5);
+			if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+				outb_p(i, address + 5);
+				return 0;
+			}
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm78_{read,write}_value. */
+
+	if (!(new_client = kmalloc((sizeof(struct i2c_client)) +
+				   sizeof(struct lm78_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct lm78_data *) (new_client + 1);
+	if (is_isa)
+		init_MUTEX(&data->lock);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm78_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80)
+			goto ERROR1;
+		if (!is_isa
+		    && (lm78_read_value(new_client, LM78_REG_I2C_ADDR) !=
+			address)) goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = lm78_read_value(new_client, LM78_REG_CHIPID);
+		if (i == 0x00)
+			kind = lm78;
+		else if (i == 0x40)
+			kind = lm78j;
+		else if ((i & 0xfe) == 0xc0)
+			kind = lm79;
+		else {
+			if (kind == 0)
+				printk
+				    ("lm78.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == lm78) {
+		type_name = "lm78";
+		client_name = "LM78 chip";
+	} else if (kind == lm78j) {
+		type_name = "lm78-j";
+		client_name = "LM78-J chip";
+	} else if (kind == lm79) {
+		type_name = "lm79";
+		client_name = "LM79 chip";
+	} else {
+#ifdef DEBUG
+		printk("lm78.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	if (is_isa)
+		request_region(address, LM78_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;
+
+	new_client->id = lm78_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					lm78_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the LM78 chip */
+	lm78_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:
+	if (is_isa)
+		release_region(address, LM78_EXTENT);
+      ERROR1:
+	kfree(new_client);
+      ERROR0:
+	return err;
+}
+
+int lm78_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct lm78_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm78.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	if i2c_is_isa_client
+		(client)
+		    release_region(client->addr, LM78_EXTENT);
+	kfree(client);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void lm78_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void lm78_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+/* The SMBus locks itself, but ISA access must be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the LM78 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+int lm78_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct lm78_data *) (client->data))->lock));
+		outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
+		res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
+		up(&(((struct lm78_data *) (client->data))->lock));
+		return res;
+	} else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* The SMBus locks itself, but ISA access muse be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the LM78 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct lm78_data *) (client->data))->lock));
+		outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
+		outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
+		up(&(((struct lm78_data *) (client->data))->lock));
+		return 0;
+	} else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM78. It should set limits, etc. */
+void lm78_init_client(struct i2c_client *client)
+{
+	int vid;
+
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others */
+	lm78_write_value(client, LM78_REG_CONFIG, 0x80);
+
+	vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f;
+	if (((struct lm78_data *) (client->data))->type == lm79)
+		vid |=
+		    (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4;
+	else
+		vid |= 0x10;
+	vid = VID_FROM_REG(vid);
+
+	lm78_write_value(client, LM78_REG_IN_MIN(0),
+			 IN_TO_REG(LM78_INIT_IN_MIN_0));
+	lm78_write_value(client, LM78_REG_IN_MAX(0),
+			 IN_TO_REG(LM78_INIT_IN_MAX_0));
+	lm78_write_value(client, LM78_REG_IN_MIN(1),
+			 IN_TO_REG(LM78_INIT_IN_MIN_1));
+	lm78_write_value(client, LM78_REG_IN_MAX(1),
+			 IN_TO_REG(LM78_INIT_IN_MAX_1));
+	lm78_write_value(client, LM78_REG_IN_MIN(2),
+			 IN_TO_REG(LM78_INIT_IN_MIN_2));
+	lm78_write_value(client, LM78_REG_IN_MAX(2),
+			 IN_TO_REG(LM78_INIT_IN_MAX_2));
+	lm78_write_value(client, LM78_REG_IN_MIN(3),
+			 IN_TO_REG(LM78_INIT_IN_MIN_3));
+	lm78_write_value(client, LM78_REG_IN_MAX(3),
+			 IN_TO_REG(LM78_INIT_IN_MAX_3));
+	lm78_write_value(client, LM78_REG_IN_MIN(4),
+			 IN_TO_REG(LM78_INIT_IN_MIN_4));
+	lm78_write_value(client, LM78_REG_IN_MAX(4),
+			 IN_TO_REG(LM78_INIT_IN_MAX_4));
+	lm78_write_value(client, LM78_REG_IN_MIN(5),
+			 IN_TO_REG(LM78_INIT_IN_MIN_5));
+	lm78_write_value(client, LM78_REG_IN_MAX(5),
+			 IN_TO_REG(LM78_INIT_IN_MAX_5));
+	lm78_write_value(client, LM78_REG_IN_MIN(6),
+			 IN_TO_REG(LM78_INIT_IN_MIN_6));
+	lm78_write_value(client, LM78_REG_IN_MAX(6),
+			 IN_TO_REG(LM78_INIT_IN_MAX_6));
+	lm78_write_value(client, LM78_REG_FAN_MIN(1),
+			 FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2));
+	lm78_write_value(client, LM78_REG_FAN_MIN(2),
+			 FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2));
+	lm78_write_value(client, LM78_REG_FAN_MIN(3),
+			 FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2));
+	lm78_write_value(client, LM78_REG_TEMP_OVER,
+			 TEMP_TO_REG(LM78_INIT_TEMP_OVER));
+	lm78_write_value(client, LM78_REG_TEMP_HYST,
+			 TEMP_TO_REG(LM78_INIT_TEMP_HYST));
+
+	/* Start monitoring */
+	lm78_write_value(client, LM78_REG_CONFIG,
+			 (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7)
+			 | 0x01);
+
+}
+
+void lm78_update_client(struct i2c_client *client)
+{
+	struct lm78_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting lm78 update\n");
+#endif
+		for (i = 0; i <= 6; i++) {
+			data->in[i] =
+			    lm78_read_value(client, LM78_REG_IN(i));
+			data->in_min[i] =
+			    lm78_read_value(client, LM78_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm78_read_value(client, LM78_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->fan[i - 1] =
+			    lm78_read_value(client, LM78_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    lm78_read_value(client, LM78_REG_FAN_MIN(i));
+		}
+		data->temp = lm78_read_value(client, LM78_REG_TEMP);
+		data->temp_over =
+		    lm78_read_value(client, LM78_REG_TEMP_OVER);
+		data->temp_hyst =
+		    lm78_read_value(client, LM78_REG_TEMP_HYST);
+		i = lm78_read_value(client, LM78_REG_VID_FANDIV);
+		data->vid = i & 0x0f;
+		if (data->type == lm79)
+			data->vid |=
+			    (lm78_read_value(client, LM78_REG_CHIPID) &
+			     0x01) << 4;
+		else
+			data->vid |= 0x10;
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = lm78_read_value(client, LM78_REG_ALARM1) +
+		    (lm78_read_value(client, LM78_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+
+		data->fan_div[2] = 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 lm78_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int nr = ctl_name - LM78_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_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]);
+			lm78_write_value(client, LM78_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			lm78_write_value(client, LM78_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+	}
+}
+
+void lm78_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int nr = ctl_name - LM78_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_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]));
+			lm78_write_value(client, LM78_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void lm78_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_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);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			lm78_write_value(client, LM78_REG_TEMP_OVER,
+					 data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			lm78_write_value(client, LM78_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+void lm78_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void lm78_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_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 = lm78_read_value(client, LM78_REG_VID_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);
+			lm78_write_value(client, LM78_REG_VID_FANDIV, old);
+		}
+	}
+}
+
+int __init sensors_lm78_init(void)
+{
+	int res;
+
+	printk("lm78.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	lm78_initialized = 0;
+
+	if ((res = i2c_add_driver(&lm78_driver))) {
+		printk
+		    ("lm78.o: Driver registration failed, module not inserted.\n");
+		lm78_cleanup();
+		return res;
+	}
+	lm78_initialized++;
+	return 0;
+}
+
+int __init lm78_cleanup(void)
+{
+	int res;
+
+	if (lm78_initialized >= 1) {
+		if ((res = i2c_del_driver(&lm78_driver))) {
+			printk
+			    ("lm78.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		lm78_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");
+
+int init_module(void)
+{
+	return sensors_lm78_init();
+}
+
+int cleanup_module(void)
+{
+	return lm78_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/eeprom.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/eeprom.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/eeprom.c	(revision 933)
@@ -0,0 +1,449 @@
+/*
+    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>
+
+    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/version.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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);
+
+static int checksum = 0;
+MODULE_PARM(checksum, "i");
+MODULE_PARM_DESC(checksum,
+		 "Only accept eeproms whose checksum is correct");
+
+
+/* Many constants specified below */
+
+/* EEPROM registers */
+#define EEPROM_REG_CHECKSUM 0x3f
+
+/* EEPROM memory types: */
+#define ONE_K		1
+#define TWO_K		2
+#define FOUR_K		3
+#define EIGHT_K		4
+#define SIXTEEN_K	5
+
+/* Conversions */
+/* Size of EEPROM in bytes */
+#define EEPROM_SIZE 128
+
+/* Each client has this additional data */
+struct eeprom_data {
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 data[EEPROM_SIZE];	/* Register values */
+	int memtype;
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_eeprom_init(void);
+static int __init eeprom_cleanup(void);
+
+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);
+static int eeprom_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+
+static void eeprom_inc_use(struct i2c_client *client);
+static void eeprom_dec_use(struct i2c_client *client);
+
+#if 0
+static int eeprom_write_value(struct i2c_client *client, u8 reg,
+			      u16 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);
+
+
+/* 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,
+	/* command */ &eeprom_command,
+	/* inc_use */ &eeprom_inc_use,
+	/* dec_use */ &eeprom_dec_use
+};
+
+/* 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, "data0-15", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL2, "data16-31", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL3, "data32-47", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL4, "data48-63", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL5, "data64-79", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL6, "data80-95", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL7, "data96-111", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL8, "data112-127", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &eeprom_contents},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata eeprom_initialized = 0;
+
+static int eeprom_id = 0;
+
+int eeprom_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, eeprom_detect);
+}
+
+/* This function is called by sensors_detect */
+int eeprom_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i, cs;
+	struct i2c_client *new_client;
+	struct eeprom_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; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("eeprom.o: eeprom_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 eeprom_{read,write}_value. */
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct eeprom_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct eeprom_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &eeprom_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is not there, unless you force
+	   the checksum to work out. */
+	if (checksum) {
+		cs = 0;
+		for (i = 0; i <= 0x3e; i++)
+			cs += i2c_smbus_read_byte_data(new_client, i);
+		cs &= 0xff;
+		if (i2c_smbus_read_byte_data
+		    (new_client, EEPROM_REG_CHECKSUM) != cs)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = eeprom;
+
+	if (kind == eeprom) {
+		type_name = "eeprom";
+		client_name = "EEPROM chip";
+	} else {
+#ifdef DEBUG
+		printk("eeprom.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);
+
+	new_client->id = eeprom_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client, type_name,
+					eeprom_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(new_client);
+      ERROR0:
+	return err;
+}
+
+int eeprom_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int eeprom_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void eeprom_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void eeprom_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+#if 0
+/* No writes yet (PAE) */
+int eeprom_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == EEPROM_REG_CONF)
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg, value);
+	*/return 0;
+}
+#endif
+
+void eeprom_update_client(struct i2c_client *client)
+{
+	struct eeprom_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 300 * HZ) |
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting eeprom update\n");
+#endif
+
+		if (i2c_smbus_write_byte(client, 0)) {
+#ifdef DEBUG
+			printk("eeprom read start has failed!\n");
+#endif
+		}
+		for (i = 0; i < EEPROM_SIZE; i++) {
+			data->data[i] = (u8) i2c_smbus_read_byte(client);
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void eeprom_contents(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	int base = 0;
+	struct eeprom_data *data = client->data;
+
+	if (ctl_name == EEPROM_SYSCTL2) {
+		base = 16;
+	}
+	if (ctl_name == EEPROM_SYSCTL3) {
+		base = 32;
+	}
+	if (ctl_name == EEPROM_SYSCTL4) {
+		base = 48;
+	}
+	if (ctl_name == EEPROM_SYSCTL5) {
+		base = 64;
+	}
+	if (ctl_name == EEPROM_SYSCTL6) {
+		base = 80;
+	}
+	if (ctl_name == EEPROM_SYSCTL7) {
+		base = 96;
+	}
+	if (ctl_name == EEPROM_SYSCTL8) {
+		base = 112;
+	}
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		eeprom_update_client(client);
+		for (i = 0; i < 16; i++) {
+			results[i] = data->data[i + base];
+		}
+#ifdef DEBUG
+		printk("eeprom.o: 0x%X EEPROM Contents (base %d): ",
+		       client->addr, base);
+		for (i = 0; i < 16; i++) {
+			printk(" 0x%X", data->data[i + base]);
+		}
+		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");
+	}
+}
+
+int __init sensors_eeprom_init(void)
+{
+	int res;
+
+	printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	eeprom_initialized = 0;
+	if ((res = i2c_add_driver(&eeprom_driver))) {
+		printk
+		    ("eeprom.o: Driver registration failed, module not inserted.\n");
+		eeprom_cleanup();
+		return res;
+	}
+	eeprom_initialized++;
+	return 0;
+}
+
+int __init eeprom_cleanup(void)
+{
+	int res;
+
+	if (eeprom_initialized >= 1) {
+		if ((res = i2c_del_driver(&eeprom_driver))) {
+			printk
+			    ("eeprom.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+	} else
+		eeprom_initialized--;
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("EEPROM driver");
+
+int init_module(void)
+{
+	return sensors_eeprom_init();
+}
+
+int cleanup_module(void)
+{
+	return eeprom_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/mtp008.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/mtp008.c	(revision 947)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/mtp008.c	(revision 947)
@@ -0,0 +1,1217 @@
+/*
+   mtp008.c - Part of lm_sensors, Linux kernel modules for hardware
+   monitoring
+   Copyright (c) 2000  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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include "version.h"
+#include "i2c-isa.h"
+#include "sensors.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* 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 */
+#define MTP008_REG_PWM_CTRL(nr)		(0x53 + (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
+
+/*
+ * Conversion routines and macros.  Rounding and 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) / 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)
+ */
+extern 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.
+ */
+#define PWM_FROM_REG(val)	(val)
+#define PWM_TO_REG(val)		(val)
+
+/* Initial limits */
+#define MTP008_INIT_IN_0	(vid)				/* VCore 1 */
+#define MTP008_INIT_IN_1	330				/* +3.3V */
+#define MTP008_INIT_IN_2	(1200 * 10 / 38)		/* +12V */
+#define MTP008_INIT_IN_3	(vid)				/* VCore 2 */
+#define MTP008_INIT_IN_5	((11861 + 7 * (-1200)) / 36)	/* -12V */
+#define MTP008_INIT_IN_6	150				/* Vtt */
+
+#define MTP008_INIT_IN_PCT	10
+
+#define MTP008_INIT_IN_MIN_0	(MTP008_INIT_IN_0 -			      \
+				 MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_0	(MTP008_INIT_IN_0 +			      \
+				 MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MIN_1	(MTP008_INIT_IN_1 -			      \
+				 MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_1	(MTP008_INIT_IN_1 +			      \
+				 MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MIN_2	(MTP008_INIT_IN_2 -			      \
+				 MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_2	(MTP008_INIT_IN_2 +			      \
+				 MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MIN_3	(MTP008_INIT_IN_3 -			      \
+				 MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_3	(MTP008_INIT_IN_3 +			      \
+				 MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100)
+
+#define MTP008_INIT_IN_MIN_5	(MTP008_INIT_IN_5 -			      \
+				 MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_5	(MTP008_INIT_IN_5 +			      \
+				 MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MIN_6	(MTP008_INIT_IN_6 -			      \
+				 MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100)
+#define MTP008_INIT_IN_MAX_6	(MTP008_INIT_IN_6 +			      \
+				 MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100)
+
+#define MTP008_INIT_FAN_MIN_1	3000
+#define MTP008_INIT_FAN_MIN_2	3000
+#define MTP008_INIT_FAN_MIN_3	3000
+
+#define MTP008_INIT_TEMP_OVER	700				/* 70 Celsius */
+#define MTP008_INIT_TEMP_HYST	50				/* 5 Celsius */
+#define MTP008_INIT_TEMP2_OVER	70				/* 70 Celsius */
+#define MTP008_INIT_TEMP2_HYST	50				/* 5 Celsius */
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+/*
+ * 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 {
+	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 */
+};
+
+#ifdef MODULE
+static int __init sensors_mtp008_init(void);
+#else
+extern int __init sensors_mtp008_init(void);
+#endif
+static int __init mtp008_cleanup(void);
+
+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_command(struct i2c_client *client, unsigned int cmd,
+			  void *arg);
+static void mtp008_inc_use(struct i2c_client *client);
+static void mtp008_dec_use(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 int mtp008_id = 0;
+
+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,
+    /* command */		&mtp008_command,
+    /* inc_use */		&mtp008_inc_use,
+    /* dec_use */		&mtp008_dec_use
+};
+
+/* Used by mtp008_init/cleanup */
+static int __initdata mtp008_initialized = 0;
+
+/* 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,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_temp},
+	{MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+       &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+       &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_vid},
+	{MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_fan_div},
+	{MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_alarms},
+	{MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_beep},
+	{MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL,
+	 &sensors_proc_real, &sensors_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)
+ */
+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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct mtp008_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+	data = (struct mtp008_data *) (new_client + 1);
+	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;
+
+	new_client->id = mtp008_id++;
+	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 = sensors_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(new_client);
+
+      ERROR0:
+	return err;
+}
+
+int mtp008_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+}
+
+/* No commands defined yet */
+int mtp008_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+void mtp008_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void mtp008_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+int mtp008_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg) & 0xff;
+}
+
+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. It should set limits, etc. */
+void mtp008_init_client(struct i2c_client *client)
+{
+	int vid;
+
+	/*
+	 * Initialize the Myson MTP008 hardware monitoring chip.
+	 */
+	mtp008_write_value(
+				  client, MTP008_REG_CONFIG,
+	     (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80
+	    );
+
+	/*
+	 * Retrieve the VID setting (needed for the default limits).
+	 */
+	vid = mtp008_read_value(client, MTP008_REG_VID_FANDIV) & 0x0f;
+	vid |= (mtp008_read_value(client, MTP008_REG_RESET_VID4) & 0x01) << 4;
+	vid = VID_FROM_REG(vid);
+
+	/*
+	 * Set the default limits.  We are making the initial assumption that
+	 * AIN4 is to be configured as PII temp sensor 2, since we've only seen
+	 * this hardware monitoring chip on the Tyan S1824D motherboard which
+	 * is a dual PIII motherboard.  We are also making the assumption that
+	 * usually AIN5 is hooked up to the -12V sensing circuit.
+	 *
+	 * Setting temp sensors 1 and 2 to be a PII sensors is done as follows:
+	 *
+	 *          Register 0x57: 0 0 0 0 1 1 0 0
+	 *                                 | \ / +-- AIN5/VT3 with AIN5
+	 *                                 |  +----- AIN4/VT2/PII with PII
+	 *                                 +-------- VT1/PII with PII
+	 */
+	mtp008_write_value(client, MTP008_REG_PIN_CTRL2, 0x0c);
+
+	mtp008_write_value(client, MTP008_REG_IN_MAX(0),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_0));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(0),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_0));
+	mtp008_write_value(client, MTP008_REG_IN_MAX(1),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_1));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(1),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_1));
+	mtp008_write_value(client, MTP008_REG_IN_MAX(2),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_2));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(2),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_2));
+	mtp008_write_value(client, MTP008_REG_IN_MAX(3),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_3));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(3),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_3));
+
+	mtp008_write_value(client, MTP008_REG_IN_MAX(5),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_5));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(5),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_5));
+	mtp008_write_value(client, MTP008_REG_IN_MAX(6),
+			   IN_TO_REG(MTP008_INIT_IN_MAX_6));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(6),
+			   IN_TO_REG(MTP008_INIT_IN_MIN_6));
+
+	mtp008_write_value(client, MTP008_REG_TEMP_MAX,
+			   TEMP_TO_REG(MTP008_INIT_TEMP_OVER));
+	mtp008_write_value(client, MTP008_REG_TEMP_MIN,
+			   TEMP_TO_REG(MTP008_INIT_TEMP_HYST));
+	mtp008_write_value(client, MTP008_REG_IN_MAX(4),
+			   TEMP_TO_REG(MTP008_INIT_TEMP2_OVER));
+	mtp008_write_value(client, MTP008_REG_IN_MIN(4),
+			   TEMP_TO_REG(MTP008_INIT_TEMP2_HYST));
+
+	mtp008_write_value(client, MTP008_REG_FAN_MIN(1),
+			   FAN_TO_REG(MTP008_INIT_FAN_MIN_1, 2));
+	mtp008_write_value(client, MTP008_REG_FAN_MIN(2),
+			   FAN_TO_REG(MTP008_INIT_FAN_MIN_2, 2));
+	mtp008_write_value(client, MTP008_REG_FAN_MIN(3),
+			   FAN_TO_REG(MTP008_INIT_FAN_MIN_3, 2));
+
+	/*
+	 * Start monitoring.
+	 */
+	mtp008_write_value(
+		client, MTP008_REG_CONFIG,
+		(mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01
+	);
+}
+
+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 fan sensors.
+		 */
+		for (i = 0; i < 3; i++) {
+			data->fan[i] = mtp008_read_value(client,
+						  MTP008_REG_FAN(i + 1));
+			data->fan_min[i] = mtp008_read_value(client,
+					      MTP008_REG_FAN_MIN(i + 1));
+		}
+
+		/*
+		 * 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 PWN registers.
+		 */
+		for (i = 0; i < 3; i++)
+			data->pwm[i] =
+				mtp008_read_value(client,
+						  MTP008_REG_PWM_CTRL(i));
+
+		/*
+		 * Read the sensor configuration.
+		 */
+		inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2) & 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 */
+
+		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 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);
+
+		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;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		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),
+					   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);
+
+		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]);
+
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->in_max[nr] = TEMP_TO_REG(results[0]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MAX,
+					   data->in_max[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_min[nr] = TEMP_TO_REG(results[1]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MIN,
+					   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]);
+
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			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 = 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];
+
+		*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 2:	/* Thermistor */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT1_PII
+					);
+					data->sens[nr] = 2;
+					break;
+				case 3:	/* PII/Celeron thermal diode */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT1_PII
+					);
+					data->sens[nr] = 3;
+					break;
+				}
+
+				break;
+			case 2:	/* AIN, VT or PII */
+				tmp &= ~MTP008_CFG_VT2_MASK;
+				opts = "1, 2 or 3";
+
+				switch (results[0]) {
+				case 1:	/* Analog input */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_AIN
+					);
+					data->sens[nr] = 1;
+					break;
+				case 2:	/* Thermistor */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_VT
+					);
+					data->sens[nr] = 2;
+					break;
+				case 3:	/* PII/Celeron thermal diode */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_PII
+					);
+					data->sens[nr] = 3;
+					break;
+				}
+
+				break;
+			case 3:	/* AIN or VT */
+				opts = "1 or 2";
+
+				switch (results[0]) {
+				case 1:	/* Analog input */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT3_VT
+					);
+					data->sens[nr] = 1;
+					break;
+				case 2:	/* Thermistor */
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT3_VT
+					);
+					data->sens[nr] = 2;
+					break;
+				}
+
+				break;
+			}
+
+			printk("mtp008.o: Invalid sensor type %ld "
+			       "for sensor %d; must be %s.\n",
+			       results[0], nr, opts);
+		}
+	}
+}
+
+int __init sensors_mtp008_init(void)
+{
+	int res;
+
+	printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	mtp008_initialized = 0;
+
+	if ((res = i2c_add_driver(&mtp008_driver))) {
+		printk("mtp008.o: Driver registration failed, "
+		       "module not inserted.\n");
+		mtp008_cleanup();
+		return res;
+	}
+	mtp008_initialized++;
+
+	return 0;
+}
+
+int __init mtp008_cleanup(void)
+{
+	int res;
+
+	if (mtp008_initialized >= 1) {
+		if ((res = i2c_del_driver(&mtp008_driver))) {
+			printk("mtp008.o: Driver deregistration failed, "
+			       "module not removed.\n");
+			return res;
+		}
+		mtp008_initialized--;
+	}
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Kris Van Hees <aedil@alchar.org>");
+MODULE_DESCRIPTION("MTP008 driver");
+
+int init_module(void)
+{
+	return sensors_mtp008_init();
+}
+
+int cleanup_module(void)
+{
+	return mtp008_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/gl520sm.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/gl520sm.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/gl520sm.c	(revision 933)
@@ -0,0 +1,924 @@
+/*
+    gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                              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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+#define THIS_MODULE NULL
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 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(gl520sm);
+
+/* Many GL520 constants specified below 
+One of the inputs can be configured as either temp or voltage.
+That's why _TEMP2 and _VIN4 access the same register 
+*/
+
+/* The GL520 registers */
+#define GL520_REG_CHIP_ID 0x00
+#define GL520_REG_REVISION 0x01
+#define GL520_REG_VID 0x02
+#define GL520_REG_CONF 0x03
+#define GL520_REG_TEMP1 0x04
+#define GL520_REG_TEMP1_OVER 0x05
+#define GL520_REG_TEMP1_HYST 0x06
+#define GL520_REG_FAN_COUNT 0x07
+#define GL520_REG_FAN_LIMIT 0x08
+#define GL520_REG_VIN1_LIMIT 0x09
+#define GL520_REG_VIN2_LIMIT 0x0a
+#define GL520_REG_VIN3_LIMIT 0x0b
+#define GL520_REG_VDD_LIMIT 0x0c
+#define GL520_REG_VIN3 0x0d
+#define GL520_REG_VIN4 0x0e
+#define GL520_REG_TEMP2 0x0e
+#define GL520_REG_MISC 0x0f
+#define GL520_REG_ALARM 0x10
+#define GL520_REG_MASK 0x11
+#define GL520_REG_INT 0x12
+#define GL520_REG_VIN2 0x13
+#define GL520_REG_VIN1 0x14
+#define GL520_REG_VDD 0x15
+#define GL520_REG_TEMP2_OVER 0x17
+#define GL520_REG_VIN4_MAX 0x17
+#define GL520_REG_TEMP2_HYST 0x18
+#define GL520_REG_VIN4_MIN 0x18
+
+
+/* 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_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+130),\
+                                        0,255))
+#define TEMP_FROM_REG(val) (((val) - 130) * 10)
+
+extern inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) \
+ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) )
+
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255))
+#define IN_FROM_REG(val) (((val)*19)/10)
+
+#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255))
+#define VDD_FROM_REG(val) (((val)*23)/10)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+#define ALARMS_FROM_REG(val) val
+
+#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
+
+#define BEEPS_TO_REG(val) (val)
+#define BEEPS_FROM_REG(val) (val)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* Initial values */
+#define GL520_INIT_TEMP_OVER 600
+#define GL520_INIT_TEMP_HYST 500
+#define GL520_INIT_FAN_MIN_1 3000
+#define GL520_INIT_FAN_MIN_2 3000
+
+/* These are somewhat sane */
+#define GL520_INIT_VIN_1 330	/* 3.3 V */
+#define GL520_INIT_VIN_2 286	/* 12 V */
+#define GL520_INIT_VIN_3 260	/* Vcore */
+#define GL520_INIT_VIN_4 160	/* -12 V */
+#define GL520_INIT_VDD 500	/* 5 V */
+
+#define GL520_INIT_PERCENTAGE 10
+
+#define GL520_INIT_VIN_MIN_1 \
+        (GL520_INIT_VIN_1 - GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VIN_MAX_1 \
+        (GL520_INIT_VIN_1 + GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VIN_MIN_2 \
+        (GL520_INIT_VIN_2 - GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VIN_MAX_2 \
+        (GL520_INIT_VIN_2 + GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VIN_MIN_3 \
+        (GL520_INIT_VIN_3 - GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VIN_MAX_3 \
+        (GL520_INIT_VIN_3 + GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VDD_MIN \
+        (GL520_INIT_VDD - GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100)
+#define GL520_INIT_VDD_MAX \
+        (GL520_INIT_VDD + GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100)
+
+
+/* Each client has this additional data */
+struct gl520_data {
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	unsigned long last_updated_v00;
+	/* In jiffies (used only by rev00 chips) */
+
+	u8 voltage[5];		/* Register values; [0] = VDD */
+	u8 voltage_min[5];	/* Register values; [0] = VDD */
+	u8 voltage_max[5];	/* Register values; [0] = VDD */
+	u8 fan[2];
+	u8 fan_min[2];
+	u8 temp[2];		/* Register values */
+	u8 temp_over[2];	/* Register values */
+	u8 temp_hyst[2];	/* Register values */
+	u8 alarms, beeps, vid;	/* Register value */
+	u8 alarm_mask;		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u8 beep_enable;		/* Boolean */
+	u8 two_temps;		/* Boolean */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_gl520_init(void);
+static int __init gl520_cleanup(void);
+static int gl520_attach_adapter(struct i2c_adapter *adapter);
+static int gl520_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void gl520_init_client(struct i2c_client *client);
+static int gl520_detach_client(struct i2c_client *client);
+static int gl520_command(struct i2c_client *client, unsigned int cmd,
+			 void *arg);
+static void gl520_inc_use(struct i2c_client *client);
+static void gl520_dec_use(struct i2c_client *client);
+static u16 swap_bytes(u16 val);
+static int gl520_read_value(struct i2c_client *client, u8 reg);
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void gl520_update_client(struct i2c_client *client);
+
+static void gl520_vin(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_vid(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_temp(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan_div(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl520_alarms(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void gl520_beep(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan1off(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl520_config(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 gl520_driver = {
+	/* name */ "GL520SM sensor chip driver",
+	/* id */ I2C_DRIVERID_GL520,
+	/* flags */ I2C_DF_NOTIFY,
+	/* attach_adapter */ &gl520_attach_adapter,
+	/* detach_client */ &gl520_detach_client,
+	/* command */ &gl520_command,
+	/* inc_use */ &gl520_inc_use,
+	/* dec_use */ &gl520_dec_use
+};
+
+/* These files are created for each detected GL520. 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 gl520_dir_table_template[] = {
+	{GL520_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN4, "vin4", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_vid},
+	{GL520_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_fan},
+	{GL520_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_fan},
+	{GL520_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_temp},
+	{GL520_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_temp},
+	{GL520_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_fan_div},
+	{GL520_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_alarms},
+	{GL520_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_beep},
+	{GL520_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_fan1off},
+	{GL520_SYSCTL_CONFIG, "config", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &gl520_config},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata gl520_initialized = 0;
+
+/* I choose here for semi-static GL520SM allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int gl520_id = 0;
+
+int gl520_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, gl520_detect);
+}
+
+static int gl520_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct gl520_data *data;
+	int err = 0;
+	const char *type_name = "";
+	char client_name[32];
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; sensors_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("gl520sm.o: gl520_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))
+		    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 gl520_{read,write}_value. */
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct gl520_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct gl520_data *) (new_client + 1);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &gl520_driver;
+	new_client->flags = 0;
+
+	/* Determine the chip type. */
+
+	if (gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) {
+		printk
+		    ("gl520sm.o: Ignoring 'force' parameter for unknown chip at "
+		     "adapter %d, address 0x%02x\n",
+		     i2c_adapter_id(adapter), address);
+		goto ERROR1;
+	} else {
+		kind = gl520sm;
+	}
+
+	i = gl520_read_value(new_client, GL520_REG_REVISION);
+	if (kind == gl520sm) {
+		type_name = "gl520sm";
+		sprintf(client_name, "GL520SM Revision %02x chip", i);
+	} else {
+#ifdef DEBUG
+		printk("gl520sm.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->type = kind;
+
+	new_client->id = gl520_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_register_entry(new_client,
+					type_name,
+					gl520_dir_table_template,
+					THIS_MODULE)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the GL520SM chip */
+	data->two_temps = 1;
+	data->alarm_mask = 0xff;
+	gl520_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(new_client);
+      ERROR0:
+	return err;
+}
+
+
+/* Called when we have found a new GL520SM. It should set limits, etc. */
+void gl520_init_client(struct i2c_client *client)
+{
+	/* Power-on defaults (bit 7=1) */
+	gl520_write_value(client, GL520_REG_CONF, 0x80);
+
+	/* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
+	   standby mode (bit6=0) */
+	gl520_write_value(client, GL520_REG_CONF, 0x04);
+
+	/* Never interrupts */
+	gl520_write_value(client, GL520_REG_MASK, 0x00);
+
+	gl520_write_value(client, GL520_REG_TEMP1_HYST,
+			  TEMP_TO_REG(GL520_INIT_TEMP_HYST));
+	gl520_write_value(client, GL520_REG_TEMP1_OVER,
+			  TEMP_TO_REG(GL520_INIT_TEMP_OVER));
+
+	/* We set Temp2, but not Vin4. */
+	gl520_write_value(client, GL520_REG_TEMP2_HYST,
+			  TEMP_TO_REG(GL520_INIT_TEMP_HYST));
+	gl520_write_value(client, GL520_REG_TEMP2_OVER,
+			  TEMP_TO_REG(GL520_INIT_TEMP_OVER));
+
+	gl520_write_value(client, GL520_REG_MISC, (DIV_TO_REG(2) << 6) |
+			  (DIV_TO_REG(2) << 4));
+	gl520_write_value(client, GL520_REG_FAN_LIMIT,
+			  (FAN_TO_REG(GL520_INIT_FAN_MIN_1, 2) << 8) |
+			  FAN_TO_REG(GL520_INIT_FAN_MIN_2, 2));
+
+	gl520_write_value(client, GL520_REG_VIN1_LIMIT,
+			  (IN_TO_REG(GL520_INIT_VIN_MAX_1) << 8) |
+			  IN_TO_REG(GL520_INIT_VIN_MIN_1));
+	gl520_write_value(client, GL520_REG_VIN2_LIMIT,
+			  (IN_TO_REG(GL520_INIT_VIN_MAX_2) << 8) |
+			  IN_TO_REG(GL520_INIT_VIN_MIN_2));
+	gl520_write_value(client, GL520_REG_VIN3_LIMIT,
+			  (IN_TO_REG(GL520_INIT_VIN_MAX_3) << 8) |
+			  IN_TO_REG(GL520_INIT_VIN_MIN_3));
+	gl520_write_value(client, GL520_REG_VDD_LIMIT,
+			  (VDD_TO_REG(GL520_INIT_VDD_MAX) << 8) |
+			  VDD_TO_REG(GL520_INIT_VDD_MIN));
+
+	/* Clear status register (bit 5=1), start (bit6=1) */
+	gl520_write_value(client, GL520_REG_CONF, 0x24);
+	gl520_write_value(client, GL520_REG_CONF, 0x44);
+}
+
+int gl520_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_deregister_entry(((struct gl520_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("gl520sm.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+
+/* No commands defined yet */
+int gl520_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void gl520_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void gl520_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+u16 swap_bytes(u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL520 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int gl520_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return swap_bytes(i2c_smbus_read_word_data(client, reg));
+	else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL520 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return i2c_smbus_write_word_data(client, reg,
+						 swap_bytes(value));
+	else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+void gl520_update_client(struct i2c_client *client)
+{
+	struct gl520_data *data = client->data;
+	int val;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting gl520 update\n");
+#endif
+
+		data->alarms = gl520_read_value(client, GL520_REG_INT);
+		data->beeps = gl520_read_value(client, GL520_REG_ALARM);
+		data->vid = gl520_read_value(client, GL520_REG_VID) & 0x1f;
+
+		val = gl520_read_value(client, GL520_REG_VDD_LIMIT);
+		data->voltage_min[0] = val & 0xff;
+		data->voltage_max[0] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN1_LIMIT);
+		data->voltage_min[1] = val & 0xff;
+		data->voltage_max[1] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN2_LIMIT);
+		data->voltage_min[2] = val & 0xff;
+		data->voltage_max[2] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN3_LIMIT);
+		data->voltage_min[3] = val & 0xff;
+		data->voltage_max[3] = (val >> 8) & 0xff;
+
+		val = gl520_read_value(client, GL520_REG_FAN_COUNT);
+		data->fan[0] = (val >> 8) & 0xff;
+		data->fan[1] = val & 0xff;
+
+		val = gl520_read_value(client, GL520_REG_FAN_LIMIT);
+		data->fan_min[0] = (val >> 8) & 0xff;
+		data->fan_min[1] = val & 0xff;
+
+		data->temp[0] = gl520_read_value(client, GL520_REG_TEMP1);
+		data->temp_over[0] =
+		    gl520_read_value(client, GL520_REG_TEMP1_OVER);
+		data->temp_hyst[0] =
+		    gl520_read_value(client, GL520_REG_TEMP1_HYST);
+
+		val = gl520_read_value(client, GL520_REG_MISC);
+		data->fan_div[0] = (val >> 6) & 0x03;
+		data->fan_div[1] = (val >> 4) & 0x03;
+
+		data->alarms &= data->alarm_mask;
+
+		val = gl520_read_value(client, GL520_REG_CONF);
+		data->beep_enable = (val >> 2) & 1;
+
+		data->voltage[0] = gl520_read_value(client, GL520_REG_VDD);
+		data->voltage[1] =
+		    gl520_read_value(client, GL520_REG_VIN1);
+		data->voltage[2] =
+		    gl520_read_value(client, GL520_REG_VIN2);
+		data->voltage[3] =
+		    gl520_read_value(client, GL520_REG_VIN3);
+
+		/* Temp1 and Vin4 are the same input */
+		data->temp[1] = gl520_read_value(client, GL520_REG_TEMP2);
+		data->temp_over[1] =
+		    gl520_read_value(client, GL520_REG_TEMP2_OVER);
+		data->temp_hyst[1] =
+		    gl520_read_value(client, GL520_REG_TEMP2_HYST);
+
+		data->voltage[4] =
+		    gl520_read_value(client, GL520_REG_VIN4);
+		data->voltage_min[4] =
+		    gl520_read_value(client, GL520_REG_VIN4_MIN);
+		data->voltage_max[4] =
+		    gl520_read_value(client, GL520_REG_VIN4_MAX);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void gl520_temp(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_TEMP1;
+	int regnr;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_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_REG(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if ((nr == 0) && (!data->two_temps))
+			return;
+		regnr =
+		    nr == 0 ? GL520_REG_TEMP1_OVER : GL520_REG_TEMP2_OVER;
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[nr]);
+			gl520_write_value(client, regnr,
+					  data->temp_over[nr]);
+		}
+		regnr =
+		    nr == 0 ? GL520_REG_TEMP1_HYST : GL520_REG_TEMP2_HYST;
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[nr]);
+			gl520_write_value(client, regnr,
+					  data->temp_hyst[nr]);
+		}
+	}
+}
+
+void gl520_vin(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_VDD;
+	int regnr, old = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) :
+		    VDD_FROM_REG(data->voltage_min[nr]);
+		results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) :
+		    VDD_FROM_REG(data->voltage_max[nr]);
+		results[2] = nr ? IN_FROM_REG(data->voltage[nr]) :
+		    VDD_FROM_REG(data->voltage[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (nr != 4) {
+			regnr =
+			    nr == 0 ? GL520_REG_VDD_LIMIT : nr ==
+			    1 ? GL520_REG_VIN1_LIMIT : nr ==
+			    2 ? GL520_REG_VIN2_LIMIT :
+			    GL520_REG_VIN3_LIMIT;
+			if (*nrels_mag == 1)
+				old =
+				    gl520_read_value(client,
+						     regnr) & 0xff00;
+			if (*nrels_mag >= 2) {
+				data->voltage_max[nr] =
+				    nr ? IN_TO_REG(results[1]) :
+				    VDD_TO_REG(results[1]);
+				old = data->voltage_max[nr] << 8;
+			}
+			if (*nrels_mag >= 1) {
+				data->voltage_min[nr] =
+				    nr ? IN_TO_REG(results[0]) :
+				    VDD_TO_REG(results[0]);
+				old |= data->voltage_min[nr];
+				gl520_write_value(client, regnr, old);
+			}
+		} else if (!data->two_temps) {
+			if (*nrels_mag == 1)
+				gl520_write_value(client,
+						  GL520_REG_VIN4_MIN,
+						  IN_TO_REG(results[0]));
+			if (*nrels_mag >= 2)
+				gl520_write_value(client,
+						  GL520_REG_VIN4_MAX,
+						  IN_TO_REG(results[1]));
+		}
+	}
+}
+
+
+void gl520_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_FAN1;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_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;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+						       DIV_FROM_REG(data->
+								    fan_div
+								    [nr]));
+			old =
+			    gl520_read_value(client, GL520_REG_FAN_LIMIT);
+
+			if (nr == 0) {
+				old =
+				    (old & 0x00ff) | (data->
+						      fan_min[nr] << 8);
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x20;
+				else
+					data->alarm_mask |= 0x20;
+			} else {
+				old = (old & 0xff00) | data->fan_min[nr];
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x40;
+				else
+					data->alarm_mask |= 0x40;
+			}
+			gl520_write_value(client, GL520_REG_FAN_LIMIT,
+					  old);
+		}
+	}
+}
+
+
+void gl520_alarms(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void gl520_beep(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			gl520_write_value(client, GL520_REG_CONF,
+					  (gl520_read_value(client,
+							    GL520_REG_CONF)
+					   & 0xfb) | (data->
+						      beep_enable << 2));
+		}
+		if (*nrels_mag >= 2) {
+			data->beeps =
+			    BEEPS_TO_REG(results[1]) & data->alarm_mask;
+			gl520_write_value(client, GL520_REG_ALARM,
+					  data->beeps);
+		}
+	}
+}
+
+
+void gl520_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_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 = gl520_read_value(client, GL520_REG_MISC);
+		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 & 0x3f) | (data->fan_div[0] << 6);
+		}
+		gl520_write_value(client, GL520_REG_MISC, old);
+	}
+}
+
+void gl520_vid(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void gl520_fan1off(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl520_read_value(client, GL520_REG_MISC) & 0x04) !=
+		     0);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl520_read_value(client,
+					     GL520_REG_MISC) & 0xfb;
+			if (results[0])
+				old |= 0x04;
+			gl520_write_value(client, GL520_REG_MISC, old);
+		}
+	}
+}
+
+void gl520_config(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl520_read_value(client, GL520_REG_CONF) & 0x10) ==
+		     0);
+		data->two_temps = results[0];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl520_read_value(client,
+					     GL520_REG_CONF) & 0xef;
+			if (!results[1]) {
+				old |= 0x10;
+				data->two_temps = 0;
+			} else {
+				data->two_temps = 1;
+			}
+			gl520_write_value(client, GL520_REG_CONF, old);
+		}
+	}
+}
+
+int __init sensors_gl520_init(void)
+{
+	int res;
+
+	printk("gl520sm.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	gl520_initialized = 0;
+	if ((res = i2c_add_driver(&gl520_driver))) {
+		printk
+		    ("gl520sm.o: Driver registration failed, module not inserted.\n");
+		gl520_cleanup();
+		return res;
+	}
+	gl520_initialized++;
+	return 0;
+}
+
+int __init gl520_cleanup(void)
+{
+	int res;
+
+	if (gl520_initialized >= 1) {
+		if ((res = i2c_del_driver(&gl520_driver))) {
+			printk
+			    ("gl520.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		gl520_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("GL520SM driver");
+
+int init_module(void)
+{
+	return sensors_gl520_init();
+}
+
+int cleanup_module(void)
+{
+	return gl520_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/chips/ltc1710.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/chips/ltc1710.c	(revision 933)
+++ /lm-sensors/tags/V2-5-5/kernel/chips/ltc1710.c	(revision 933)
@@ -0,0 +1,400 @@
+/*
+    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/version.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/i2c.h>
+#include "sensors.h"
+#include "i2c-isa.h"
+#include "version.h"
+#include <linux/init.h>
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
+    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
+#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
+#endif
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+/* 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 {
+	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 */
+};
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif				/* MODULE */
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+int __init sensors_ltc1710_init(void);
+static int __init ltc1710_cleanup(void);
+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 int ltc1710_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+static void ltc1710_inc_use(struct i2c_client *client);
+static void ltc1710_dec_use(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,
+	/* command */ &ltc1710_command,
+	/* inc_use */ &ltc1710_inc_use,
+	/* dec_use */ &ltc1710_dec_use
+};
+
+/* 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, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &ltc1710_switch1},
+	{LTC1710_SYSCTL_SWITCH_2, "switch2", NULL, 0, 0644, NULL, &sensors_proc_real,
+	 &sensors_sysctl_real, NULL, &ltc1710_switch2},
+	{0}
+};
+
+/* Used by init/cleanup */
+static int __initdata ltc1710_initialized = 0;
+
+/* I choose here for semi-static LTC1710 allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+static int ltc1710_id = 0;
+
+int ltc1710_attach_adapter(struct i2c_adapter *adapter)
+{
+	return sensors_detect(adapter, &addr_data, ltc1710_detect);
+}
+
+/* This function is called by sensors_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; sensors_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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
+				   sizeof(struct ltc1710_data),
+				   GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	data = (struct ltc1710_data *) (new_client + 1);
+	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);
+
+	new_client->id = ltc1710_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = sensors_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(new_client);
+      ERROR0:
+	return err;
+}
+
+
+int ltc1710_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	sensors_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);
+
+	return 0;
+
+}
+
+/* No commands defined yet */
+int ltc1710_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	return 0;
+}
+
+/* Nothing here yet */
+void ltc1710_inc_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+/* Nothing here yet */
+void ltc1710_dec_use(struct i2c_client *client)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+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);
+		}
+	}
+}
+
+int __init sensors_ltc1710_init(void)
+{
+	int res;
+
+	printk("ltc1710.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	ltc1710_initialized = 0;
+	if ((res = i2c_add_driver(&ltc1710_driver))) {
+		printk
+		    ("ltc1710.o: Driver registration failed, module not inserted.\n");
+		ltc1710_cleanup();
+		return res;
+	}
+	ltc1710_initialized++;
+	return 0;
+}
+
+int __init ltc1710_cleanup(void)
+{
+	int res;
+
+	if (ltc1710_initialized >= 1) {
+		if ((res = i2c_del_driver(&ltc1710_driver))) {
+			printk
+			    ("ltc1710.o: Driver deregistration failed, module not removed.\n");
+			return res;
+		}
+		ltc1710_initialized--;
+	}
+
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LTC1710 driver");
+
+int init_module(void)
+{
+	return sensors_ltc1710_init();
+}
+
+int cleanup_module(void)
+{
+	return ltc1710_cleanup();
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/kernel/Module.mk
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/Module.mk	(revision 681)
+++ /lm-sensors/tags/V2-5-5/kernel/Module.mk	(revision 681)
@@ -0,0 +1,46 @@
+#  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
+KERNELDIR := $(MODULE_DIR)
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+KERNELTARGETS := 
+ifneq ($(shell if grep -q '^CONFIG_SENSORS=y' $(LINUX)/.config; then echo 1; fi),1)
+KERNELTARGETS += $(MODULE_DIR)/sensors.o
+endif
+
+# Include all dependency files
+INCLUDEFILES += $(KERNELTARGETS:.o=.d)
+
+all-kernel: $(KERNELTARGETS)
+all :: all-kernel
+
+install-kernel: all-kernel
+	if [ -n "$(KERNELTARGETS)" ] ; then \
+	  $(MKDIR) $(DESTDIR)$(MODDIR) ; \
+	  $(INSTALL) -o root -g root -m 644 $(KERNELTARGETS) $(DESTDIR)$(MODDIR) ;\
+	fi
+install :: install-kernel 
+
+clean-kernel:
+	$(RM) $(KERNELDIR)/*.o $(KERNELDIR)/*.d
+clean :: clean-kernel
Index: /lm-sensors/tags/V2-5-5/kernel/sensors.c
===================================================================
--- /lm-sensors/tags/V2-5-5/kernel/sensors.c	(revision 935)
+++ /lm-sensors/tags/V2-5-5/kernel/sensors.c	(revision 935)
@@ -0,0 +1,992 @@
+/*
+    sensors.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.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+
+#include "version.h"
+#include <linux/i2c.h>
+#include "i2c-isa.h"
+#include "sensors.h"
+
+#include <linux/init.h>
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+static int sensors_cleanup(void);
+#endif				/* MODULE */
+
+#ifndef THIS_MODULE
+#define THIS_MODULE NULL
+#endif
+
+static int sensors_create_name(char **name, const char *prefix,
+			       struct i2c_adapter *adapter, int addr);
+static int sensors_parse_reals(int *nrels, void *buffer, int bufsize,
+			       long *results, int magnitude);
+static int sensors_write_reals(int nrels, void *buffer, int *bufsize,
+			       long *results, int magnitude);
+static int sensors_proc_chips(ctl_table * ctl, int write,
+			      struct file *filp, void *buffer,
+			      size_t * lenp);
+static int sensors_sysctl_chips(ctl_table * table, int *name, int nlen,
+				void *oldval, size_t * oldlenp,
+				void *newval, size_t newlen,
+				void **context);
+
+static int __init sensors_init(void);
+
+#define SENSORS_ENTRY_MAX 20
+static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX];
+
+static struct i2c_client *sensors_clients[SENSORS_ENTRY_MAX];
+static unsigned short sensors_inodes[SENSORS_ENTRY_MAX];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
+static void sensors_fill_inode(struct inode *inode, int fill);
+static void sensors_dir_fill_inode(struct inode *inode, int fill);
+#endif				/* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) */
+
+static ctl_table sysctl_table[] = {
+	{CTL_DEV, "dev", NULL, 0, 0555},
+	{0},
+	{DEV_SENSORS, "sensors", NULL, 0, 0555},
+	{0},
+	{0, NULL, NULL, 0, 0555},
+	{0}
+};
+
+static ctl_table sensors_proc_dev_sensors[] = {
+	{SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &sensors_proc_chips,
+	 &sensors_sysctl_chips},
+	{0}
+};
+
+static ctl_table sensors_proc_dev[] = {
+	{DEV_SENSORS, "sensors", NULL, 0, 0555, sensors_proc_dev_sensors},
+	{0},
+};
+
+
+static ctl_table sensors_proc[] = {
+	{CTL_DEV, "dev", NULL, 0, 0555, sensors_proc_dev},
+	{0}
+};
+
+
+static struct ctl_table_header *sensors_proc_header;
+static int sensors_initialized;
+
+/* This returns a nice name for a new directory; for example lm78-isa-0310
+   (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
+   a LM75 chip on the third i2c bus at address 0x4e).  
+   name is allocated first. */
+int sensors_create_name(char **name, const char *prefix,
+			struct i2c_adapter *adapter, int addr)
+{
+	char name_buffer[50];
+	int id;
+	if (i2c_is_isa_adapter(adapter))
+		sprintf(name_buffer, "%s-isa-%04x", prefix, addr);
+	else {
+		if ((id = i2c_adapter_id(adapter)) < 0)
+			return -ENOENT;
+		sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
+	}
+	*name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
+	strcpy(*name, name_buffer);
+	return 0;
+}
+
+/* This rather complex function must be called when you want to add an entry
+   to /proc/sys/dev/sensors/chips. It also creates a new directory within 
+   /proc/sys/dev/sensors/.
+   ctl_template should be a template of the newly created directory. It is
+   copied in memory. The extra2 field of each file is set to point to client.
+   If any driver wants subdirectories within the newly created directory,
+   this function must be updated! 
+   controlling_mod is the controlling module. It should usually be
+   THIS_MODULE when calling. Note that this symbol is not defined in
+   kernels before 2.3.13; define it to NULL in that case. We will not use it
+   for anything older than 2.3.27 anyway. */
+int sensors_register_entry(struct i2c_client *client, const char *prefix,
+			   ctl_table * ctl_template,
+			   struct module *controlling_mod)
+{
+	int i, res, len, id;
+	ctl_table *new_table;
+	char *name;
+	struct ctl_table_header *new_header;
+
+	if ((res = sensors_create_name(&name, prefix, client->adapter,
+				       client->addr))) return res;
+
+	for (id = 0; id < SENSORS_ENTRY_MAX; id++)
+		if (!sensors_entries[id]) {
+			break;
+		}
+	if (id == SENSORS_ENTRY_MAX) {
+		kfree(name);
+		return -ENOMEM;
+	}
+	id += 256;
+
+	len = 0;
+	while (ctl_template[len].procname)
+		len++;
+	len += 7;
+	if (!(new_table = kmalloc(sizeof(ctl_table) * len, GFP_KERNEL))) {
+		kfree(name);
+		return -ENOMEM;
+	}
+
+	memcpy(new_table, sysctl_table, 6 * sizeof(ctl_table));
+	new_table[0].child = &new_table[2];
+	new_table[2].child = &new_table[4];
+	new_table[4].child = &new_table[6];
+	new_table[4].procname = name;
+	new_table[4].ctl_name = id;
+	memcpy(new_table + 6, ctl_template, (len - 6) * sizeof(ctl_table));
+	for (i = 6; i < len; i++)
+		new_table[i].extra2 = client;
+
+	if (!(new_header = register_sysctl_table(new_table, 0))) {
+		kfree(new_table);
+		kfree(name);
+		return -ENOMEM;
+	}
+
+	sensors_entries[id - 256] = new_header;
+
+	sensors_clients[id - 256] = client;
+#ifdef DEBUG
+	if (!new_header || !new_header->ctl_table ||
+	    !new_header->ctl_table->child ||
+	    !new_header->ctl_table->child->child ||
+	    !new_header->ctl_table->child->child->de) {
+		printk
+		    ("sensors.o: NULL pointer when trying to install fill_inode fix!\n");
+		return id;
+	}
+#endif				/* DEBUG */
+	sensors_inodes[id - 256] =
+	    new_header->ctl_table->child->child->de->low_ino;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27))
+	new_header->ctl_table->child->child->de->owner = controlling_mod;
+#else
+	new_header->ctl_table->child->child->de->fill_inode =
+	    &sensors_dir_fill_inode;
+#endif	/* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) */
+
+	return id;
+}
+
+void sensors_deregister_entry(int id)
+{
+	ctl_table *table;
+	char *temp;
+	id -= 256;
+	if (sensors_entries[id]) {
+		table = sensors_entries[id]->ctl_table;
+		unregister_sysctl_table(sensors_entries[id]);
+		/* Below two-step kfree is needed to keep gcc happy about const points */
+		(const char *) temp = table[4].procname;
+		kfree(temp);
+		kfree(table);
+		sensors_entries[id] = NULL;
+		sensors_clients[id] = NULL;
+	}
+}
+
+/* Monitor access for /proc/sys/dev/sensors; make unloading sensors.o 
+   impossible if some process still uses it or some file in it */
+void sensors_fill_inode(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading
+   the corresponding module impossible if some process still uses it or
+   some file in it */
+void sensors_dir_fill_inode(struct inode *inode, int fill)
+{
+	int i;
+	struct i2c_client *client;
+
+#ifdef DEBUG
+	if (!inode) {
+		printk("sensors.o: Warning: inode NULL in fill_inode()\n");
+		return;
+	}
+#endif				/* def DEBUG */
+
+	for (i = 0; i < SENSORS_ENTRY_MAX; i++)
+		if (sensors_clients[i]
+		    && (sensors_inodes[i] == inode->i_ino)) break;
+#ifdef DEBUG
+	if (i == SENSORS_ENTRY_MAX) {
+		printk
+		    ("sensors.o: Warning: inode (%ld) not found in fill_inode()\n",
+		     inode->i_ino);
+		return;
+	}
+#endif				/* def DEBUG */
+	client = sensors_clients[i];
+	if (fill)
+		client->driver->inc_use(client);
+	else
+		client->driver->dec_use(client);
+}
+
+int sensors_proc_chips(ctl_table * ctl, int write, struct file *filp,
+		       void *buffer, size_t * lenp)
+{
+	char BUF[SENSORS_PREFIX_MAX + 30];
+	int buflen, curbufsize, i;
+	struct ctl_table *client_tbl;
+
+	if (write)
+		return 0;
+
+	/* If buffer is size 0, or we try to read when not at the start, we
+	   return nothing. Note that I think writing when not at the start
+	   does not work either, but anyway, this is straight from the kernel
+	   sources. */
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+	curbufsize = 0;
+	for (i = 0; i < SENSORS_ENTRY_MAX; i++)
+		if (sensors_entries[i]) {
+			client_tbl =
+			    sensors_entries[i]->ctl_table->child->child;
+			buflen =
+			    sprintf(BUF, "%d\t%s\n", client_tbl->ctl_name,
+				    client_tbl->procname);
+			if (buflen + curbufsize > *lenp)
+				buflen = *lenp - curbufsize;
+			if(copy_to_user(buffer, BUF, buflen))
+				return -EFAULT;
+			curbufsize += buflen;
+			(char *) buffer += buflen;
+		}
+	*lenp = curbufsize;
+	filp->f_pos += curbufsize;
+	return 0;
+}
+
+int sensors_sysctl_chips(ctl_table * table, int *name, int nlen,
+			 void *oldval, size_t * oldlenp, void *newval,
+			 size_t newlen, void **context)
+{
+	struct sensors_chips_data data;
+	int i, oldlen, nrels, maxels,ret=0;
+	struct ctl_table *client_tbl;
+
+	if (oldval && oldlenp && !((ret = get_user(oldlen, oldlenp))) && 
+	    oldlen) {
+		maxels = oldlen / sizeof(struct sensors_chips_data);
+		nrels = 0;
+		for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels);
+		     i++)
+			if (sensors_entries[i]) {
+				client_tbl =
+				    sensors_entries[i]->ctl_table->child->
+				    child;
+				data.sysctl_id = client_tbl->ctl_name;
+				strcpy(data.name, client_tbl->procname);
+				if(copy_to_user(oldval, &data,
+					     sizeof(struct
+						    sensors_chips_data)))
+					return -EFAULT;
+				(char *) oldval +=
+				    sizeof(struct sensors_chips_data);
+				nrels++;
+			}
+		oldlen = nrels * sizeof(struct sensors_chips_data);
+		if(put_user(oldlen, oldlenp))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+
+/* This funcion reads or writes a 'real' value (encoded by the combination
+   of an integer and a magnitude, the last is the power of ten the value
+   should be divided with) to a /proc/sys directory. To use this function,
+   you must (before registering the ctl_table) set the extra2 field to the
+   client, and the extra1 field to a function of the form:
+      void func(struct i2c_client *client, int operation, int ctl_name,
+                int *nrels_mag, long *results)
+   This function can be called for three values of operation. If operation
+   equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in 
+   nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
+   be read into results. nrels_mag should return the number of elements
+   read; the maximum number is put in it on entry. Finally, if operation
+   equals SENSORS_PROC_REAL_WRITE, the values in results should be
+   written to the chip. nrels_mag contains on entry the number of elements
+   found.
+   In all cases, client points to the client we wish to interact with,
+   and ctl_name is the SYSCTL id of the file we are accessing. */
+int sensors_proc_real(ctl_table * ctl, int write, struct file *filp,
+		      void *buffer, size_t * lenp)
+{
+#define MAX_RESULTS 32
+	int mag, nrels = MAX_RESULTS;
+	long results[MAX_RESULTS];
+	sensors_real_callback callback = ctl->extra1;
+	struct i2c_client *client = ctl->extra2;
+	int res;
+
+	/* If buffer is size 0, or we try to read when not at the start, we
+	   return nothing. Note that I think writing when not at the start
+	   does not work either, but anyway, this is straight from the kernel
+	   sources. */
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	/* Get the magnitude */
+	callback(client, SENSORS_PROC_REAL_INFO, ctl->ctl_name, &mag,
+		 NULL);
+
+	if (write) {
+		/* Read the complete input into results, converting to longs */
+		res = sensors_parse_reals(&nrels, buffer, *lenp, results, mag);
+		if (res)
+			return res;
+
+		if (!nrels)
+			return 0;
+
+		/* Now feed this information back to the client */
+		callback(client, SENSORS_PROC_REAL_WRITE, ctl->ctl_name,
+			 &nrels, results);
+
+		filp->f_pos += *lenp;
+		return 0;
+	} else {		/* read */
+		/* Get the information from the client into results */
+		callback(client, SENSORS_PROC_REAL_READ, ctl->ctl_name,
+			 &nrels, results);
+
+		/* And write them to buffer, converting to reals */
+		res = sensors_write_reals(nrels, buffer, lenp, results, mag);
+		if (res)
+			return res;
+		filp->f_pos += *lenp;
+		return 0;
+	}
+}
+
+/* This function is equivalent to sensors_proc_real, only it interacts with
+   the sysctl(2) syscall, and returns no reals, but integers */
+int sensors_sysctl_real(ctl_table * table, int *name, int nlen,
+			void *oldval, size_t * oldlenp, void *newval,
+			size_t newlen, void **context)
+{
+	long results[MAX_RESULTS];
+	int oldlen, nrels = MAX_RESULTS,ret=0;
+	sensors_real_callback callback = table->extra1;
+	struct i2c_client *client = table->extra2;
+
+	/* Check if we need to output the old values */
+	if (oldval && oldlenp && !((ret=get_user(oldlen, oldlenp))) && oldlen) {
+		callback(client, SENSORS_PROC_REAL_READ, table->ctl_name,
+			 &nrels, results);
+
+		/* Note the rounding factor! */
+		if (nrels * sizeof(long) < oldlen)
+			oldlen = nrels * sizeof(long);
+		oldlen = (oldlen / sizeof(long)) * sizeof(long);
+		if(copy_to_user(oldval, results, oldlen))
+			return -EFAULT;
+		if(put_user(oldlen, oldlenp))
+			return -EFAULT;
+	}
+
+	if (newval && newlen) {
+		/* Note the rounding factor! */
+		newlen -= newlen % sizeof(long);
+		nrels = newlen / sizeof(long);
+		if(copy_from_user(results, newval, newlen))
+			return -EFAULT;
+
+		/* Get the new values back to the client */
+		callback(client, SENSORS_PROC_REAL_WRITE, table->ctl_name,
+			 &nrels, results);
+	}
+	return ret;
+}
+
+
+/* nrels contains initially the maximum number of elements which can be
+   put in results, and finally the number of elements actually put there.
+   A magnitude of 1 will multiply everything with 10; etc.
+   buffer, bufsize is the character buffer we read from and its length.
+   results will finally contain the parsed integers. 
+
+   Buffer should contain several reals, separated by whitespace. A real
+   has the following syntax:
+     [ Minus ] Digit* [ Dot Digit* ] 
+   (everything between [] is optional; * means zero or more).
+   When the next character is unparsable, everything is skipped until the
+   next whitespace.
+
+   WARNING! This is tricky code. I have tested it, but there may still be
+            hidden bugs in it, even leading to crashes and things!
+*/
+int sensors_parse_reals(int *nrels, void *buffer, int bufsize,
+			 long *results, int magnitude)
+{
+	int maxels, min, mag;
+	long res,ret=0;
+	char nextchar = 0;
+
+	maxels = *nrels;
+	*nrels = 0;
+
+	while (bufsize && (*nrels < maxels)) {
+
+		/* Skip spaces at the start */
+		while (bufsize && 
+		       !((ret=get_user(nextchar, (char *) buffer))) &&
+		       isspace((int) nextchar)) {
+			bufsize--;
+			((char *) buffer)++;
+		}
+
+		if (ret)
+			return -EFAULT;	
+		/* Well, we may be done now */
+		if (!bufsize)
+			return 0;
+
+		/* New defaults for our result */
+		min = 0;
+		res = 0;
+		mag = magnitude;
+
+		/* Check for a minus */
+		if (!((ret=get_user(nextchar, (char *) buffer)))
+		    && (nextchar == '-')) {
+			min = 1;
+			bufsize--;
+			((char *) buffer)++;
+		}
+		if (ret)
+			return -EFAULT;
+
+		/* Digits before a decimal dot */
+		while (bufsize && 
+		       !((ret=get_user(nextchar, (char *) buffer))) &&
+		       isdigit((int) nextchar)) {
+			res = res * 10 + nextchar - '0';
+			bufsize--;
+			((char *) buffer)++;
+		}
+		if (ret)
+			return -EFAULT;
+
+		/* If mag < 0, we must actually divide here! */
+		while (mag < 0) {
+			res = res / 10;
+			mag++;
+		}
+
+		if (bufsize && (nextchar == '.')) {
+			/* Skip the dot */
+			bufsize--;
+			((char *) buffer)++;
+
+			/* Read digits while they are significant */
+			while (bufsize && (mag > 0) &&
+			       !((ret=get_user(nextchar, (char *) buffer))) &&
+			       isdigit((int) nextchar)) {
+				res = res * 10 + nextchar - '0';
+				mag--;
+				bufsize--;
+				((char *) buffer)++;
+			}
+			if (ret)
+				return -EFAULT;
+		}
+		/* If we are out of data, but mag > 0, we need to scale here */
+		while (mag > 0) {
+			res = res * 10;
+			mag--;
+		}
+
+		/* Skip everything until we hit whitespace */
+		while (bufsize && 
+		       !((ret=get_user(nextchar, (char *) buffer))) &&
+		       isspace((int) nextchar)) {
+			bufsize--;
+			((char *) buffer)++;
+		}
+		if (ret)
+			return -EFAULT;
+
+		/* Put res in results */
+		results[*nrels] = (min ? -1 : 1) * res;
+		(*nrels)++;
+	}
+
+	/* Well, there may be more in the buffer, but we need no more data. 
+	   Ignore anything that is left. */
+	return 0;
+}
+
+int sensors_write_reals(int nrels, void *buffer, int *bufsize,
+			 long *results, int magnitude)
+{
+#define BUFLEN 20
+	char BUF[BUFLEN + 1];	/* An individual representation should fit! */
+	char printfstr[10];
+	int nr = 0;
+	int buflen, mag, times;
+	int curbufsize = 0;
+
+	while ((nr < nrels) && (curbufsize < *bufsize)) {
+		mag = magnitude;
+
+		if (nr != 0) {
+			if(put_user(' ', (char *) buffer))
+				return -EFAULT;
+			curbufsize++;
+			((char *) buffer)++;
+		}
+
+		/* Fill BUF with the representation of the next string */
+		if (mag <= 0) {
+			buflen = sprintf(BUF, "%ld", results[nr]);
+			if (buflen < 0) {	/* Oops, a sprintf error! */
+				*bufsize = 0;
+				return -EINVAL;
+			}
+			while ((mag < 0) && (buflen < BUFLEN)) {
+				BUF[buflen++] = '0';
+				mag++;
+			}
+			BUF[buflen] = 0;
+		} else {
+			times = 1;
+			for (times = 1; mag-- > 0; times *= 10);
+			if (results[nr] < 0) {
+				BUF[0] = '-';
+				buflen = 1;
+			} else
+				buflen = 0;
+			strcpy(printfstr, "%ld.%0Xld");
+			printfstr[6] = magnitude + '0';
+			buflen +=
+			    sprintf(BUF + buflen, printfstr,
+				    abs(results[nr]) / times,
+				    abs(results[nr]) % times);
+			if (buflen < 0) {	/* Oops, a sprintf error! */
+				*bufsize = 0;
+				return -EINVAL;
+			}
+		}
+
+		/* Now copy it to the user-space buffer */
+		if (buflen + curbufsize > *bufsize)
+			buflen = *bufsize - curbufsize;
+		if(copy_to_user(buffer, BUF, buflen))
+			return -EFAULT;
+		curbufsize += buflen;
+		(char *) buffer += buflen;
+
+		nr++;
+	}
+	if (curbufsize < *bufsize) {
+		if(put_user('\n', (char *) buffer))
+			return -EFAULT;
+		curbufsize++;
+	}
+	*bufsize = curbufsize;
+	return 0;
+}
+
+
+/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */
+int sensors_detect(struct i2c_adapter *adapter,
+		   struct sensors_address_data *address_data,
+		   sensors_found_addr_proc * found_proc)
+{
+	int addr, i, found, j, err;
+	struct sensors_force_data *this_force;
+	int is_isa = i2c_is_isa_adapter(adapter);
+	int adapter_id =
+	    is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter);
+
+	/* Forget it if we can't probe using SMBUS_QUICK */
+	if ((!is_isa)
+	    && !i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_QUICK)) return -1;
+
+	for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
+		if ((is_isa && check_region(addr, 1)) ||
+		    (!is_isa && i2c_check_addr(adapter, addr)))
+			continue;
+
+		/* If it is in one of the force entries, we don't do any detection
+		   at all */
+		found = 0;
+		for (i = 0;
+		     !found
+		     && (this_force =
+			 address_data->forces + i, this_force->force); i++) {
+			for (j = 0;
+			     !found
+			     && (this_force->force[j] != SENSORS_I2C_END);
+			     j += 2) {
+				if (
+				    ((adapter_id == this_force->force[j])
+				     ||
+				     ((this_force->
+				       force[j] == SENSORS_ANY_I2C_BUS)
+				      && !is_isa))
+				    && (addr == this_force->force[j + 1])) {
+#ifdef DEBUG
+					printk
+					    ("sensors.o: found force parameter for adapter %d, addr %04x\n",
+					     adapter_id, addr);
+#endif
+					if (
+					    (err =
+					     found_proc(adapter, addr, 0,
+							this_force->
+							kind))) return err;
+					found = 1;
+				}
+			}
+		}
+		if (found)
+			continue;
+
+		/* If this address is in one of the ignores, we can forget about it
+		   right now */
+		for (i = 0;
+		     !found
+		     && (address_data->ignore[i] != SENSORS_I2C_END);
+		     i += 2) {
+			if (
+			    ((adapter_id == address_data->ignore[i])
+			     ||
+			     ((address_data->
+			       ignore[i] == SENSORS_ANY_I2C_BUS)
+			      && !is_isa))
+			    && (addr == address_data->ignore[i + 1])) {
+#ifdef DEBUG
+				printk
+				    ("sensors.o: found ignore parameter for adapter %d, "
+				     "addr %04x\n", adapter_id, addr);
+#endif
+				found = 1;
+			}
+		}
+		for (i = 0;
+		     !found
+		     && (address_data->ignore_range[i] != SENSORS_I2C_END);
+		     i += 3) {
+			if (
+			    ((adapter_id == address_data->ignore_range[i])
+			     ||
+			     ((address_data->
+			       ignore_range[i] ==
+			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			    && (addr >= address_data->ignore_range[i + 1])
+			    && (addr <= address_data->ignore_range[i + 2])) {
+#ifdef DEBUG
+				printk
+				    ("sensors.o: found ignore_range parameter for adapter %d, "
+				     "addr %04x\n", adapter_id, addr);
+#endif
+				found = 1;
+			}
+		}
+		if (found)
+			continue;
+
+		/* Now, we will do a detection, but only if it is in the normal or 
+		   probe entries */
+		if (is_isa) {
+			for (i = 0;
+			     !found
+			     && (address_data->normal_isa[i] !=
+				 SENSORS_ISA_END); i += 1) {
+				if (addr == address_data->normal_isa[i]) {
+#ifdef DEBUG
+					printk
+					    ("sensors.o: found normal isa entry for adapter %d, "
+					     "addr %04x\n", adapter_id,
+					     addr);
+#endif
+					found = 1;
+				}
+			}
+			for (i = 0;
+			     !found
+			     && (address_data->normal_isa_range[i] !=
+				 SENSORS_ISA_END); i += 3) {
+				if (
+				    (addr >=
+				     address_data->normal_isa_range[i])
+				    && (addr <=
+					address_data->normal_isa_range[i +
+								       1])
+				    &&
+				    ((addr
+				      -
+				      address_data->normal_isa_range[i]) %
+				     address_data->normal_isa_range[i +
+								    2] ==
+				     0)) {
+#ifdef DEBUG
+					printk
+					    ("sensors.o: found normal isa_range entry for adapter %d, "
+					     "addr %04x", adapter_id,
+					     addr);
+#endif
+					found = 1;
+				}
+			}
+		} else {
+			for (i = 0;
+			     !found
+			     && (address_data->normal_i2c[i] !=
+				 SENSORS_I2C_END); i += 1) {
+				if (addr == address_data->normal_i2c[i]) {
+					found = 1;
+#ifdef DEBUG
+					printk
+					    ("sensors.o: found normal i2c entry for adapter %d, "
+					     "addr %02x", adapter_id,
+					     addr);
+#endif
+				}
+			}
+			for (i = 0;
+			     !found
+			     && (address_data->normal_i2c_range[i] !=
+				 SENSORS_I2C_END); i += 2) {
+				if (
+				    (addr >=
+				     address_data->normal_i2c_range[i])
+				    && (addr <=
+					address_data->normal_i2c_range[i +
+								       1]))
+				{
+#ifdef DEBUG
+					printk
+					    ("sensors.o: found normal i2c_range entry for adapter %d, "
+					     "addr %04x\n", adapter_id,
+					     addr);
+#endif
+					found = 1;
+				}
+			}
+		}
+
+		for (i = 0;
+		     !found && (address_data->probe[i] != SENSORS_I2C_END);
+		     i += 2) {
+			if (((adapter_id == address_data->probe[i]) ||
+			     ((address_data->
+			       probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa))
+			    && (addr == address_data->probe[i + 1])) {
+#ifdef DEBUG
+				printk
+				    ("sensors.o: found probe parameter for adapter %d, "
+				     "addr %04x\n", adapter_id, addr);
+#endif
+				found = 1;
+			}
+		}
+		for (i = 0;
+		     !found
+		     && (address_data->probe_range[i] != SENSORS_I2C_END);
+		     i += 3) {
+			if (
+			    ((adapter_id == address_data->probe_range[i])
+			     ||
+			     ((address_data->
+			       probe_range[i] ==
+			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			    && (addr >= address_data->probe_range[i + 1])
+			    && (addr <= address_data->probe_range[i + 2])) {
+				found = 1;
+#ifdef DEBUG
+				printk
+				    ("sensors.o: found probe_range parameter for adapter %d, "
+				     "addr %04x\n", adapter_id, addr);
+#endif
+			}
+		}
+		if (!found)
+			continue;
+
+		/* OK, so we really should examine this address. First check
+		   whether there is some client here at all! */
+		if (is_isa ||
+		    (i2c_smbus_xfer
+		     (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0))
+			if ((err = found_proc(adapter, addr, 0, -1)))
+				return err;
+	}
+	return 0;
+}
+
+int __init sensors_init(void)
+{
+	printk("sensors.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	sensors_initialized = 0;
+	if (!
+	    (sensors_proc_header =
+	     register_sysctl_table(sensors_proc, 0))) return -ENOMEM;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1))
+	sensors_proc_header->ctl_table->child->de->owner = THIS_MODULE;
+#else
+	sensors_proc_header->ctl_table->child->de->fill_inode =
+	    &sensors_fill_inode;
+#endif				/* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)) */
+	sensors_initialized++;
+	return 0;
+}
+
+EXPORT_SYMBOL(sensors_deregister_entry);
+EXPORT_SYMBOL(sensors_detect);
+EXPORT_SYMBOL(sensors_proc_real);
+EXPORT_SYMBOL(sensors_register_entry);
+EXPORT_SYMBOL(sensors_sysctl_real);
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM78 driver");
+
+int sensors_cleanup(void)
+{
+	if (sensors_initialized >= 1) {
+		unregister_sysctl_table(sensors_proc_header);
+		sensors_initialized--;
+	}
+	return 0;
+}
+
+int init_module(void)
+{
+	return sensors_init();
+}
+
+int cleanup_module(void)
+{
+	return sensors_cleanup();
+}
+
+#else				/* ndef MODULE */
+
+#ifdef CONFIG_SENSORS_ADM1021
+extern int sensors_adm1021_init(void);
+#endif
+#ifdef CONFIG_SENSORS_ADM9240
+extern int sensors_adm9024_init(void);
+#endif
+#ifdef CONFIG_SENSORS_DS1621
+extern int sensors_ds1621_init(void);
+#endif
+#ifdef CONFIG_SENSORS_GL518SM
+extern int sensors_gl518sm_init(void);
+#endif
+#ifdef CONFIG_SENSORS_LM75
+extern int sensors_lm75_init(void);
+#endif
+#ifdef CONFIG_SENSORS_LM78
+extern int sensors_lm78_init(void);
+#endif
+#ifdef CONFIG_SENSORS_LM80
+extern int sensors_lm80_init(void);
+#endif
+#ifdef CONFIG_SENSORS_SIS5595
+extern int sensors_sis5595_init(void);
+#endif
+#ifdef CONFIG_SENSORS_W83781D
+extern int sensors_w83781d_init(void);
+#endif
+#ifdef CONFIG_SENSORS_EEPROM
+extern int sensors_eeprom_init(void);
+#endif
+#ifdef CONFIG_SENSORS_LTC1710
+extern int sensors_ltc1710_init(void);
+#endif
+
+int __init sensors_init_all(void)
+{
+	sensors_init();
+#ifdef CONFIG_SENSORS_ADM1021
+	sensors_adm1021_init();
+#endif
+#ifdef CONFIG_SENSORS_ADM9024
+	sensors_adm9024_init();
+#endif
+#ifdef CONFIG_SENSORS_DS1621
+	sensors_ds1621_init();
+#endif
+#ifdef CONFIG_SENSORS_GL518SM
+	sensors_gl518sm_init();
+#endif
+#ifdef CONFIG_SENSORS_LM75
+	sensors_lm75_init();
+#endif
+#ifdef CONFIG_SENSORS_LM78
+	sensors_lm78_init();
+#endif
+#ifdef CONFIG_SENSORS_LM80
+	sensors_lm80_init();
+#endif
+#ifdef CONFIG_SENSORS_SIS5595
+	sensors_sis5595_init();
+#endif
+#ifdef CONFIG_SENSORS_W83781D
+	sensors_w83781d_init();
+#endif
+#ifdef CONFIG_SENSORS_EEPROM
+	sensors_eeprom_init();
+#endif
+#ifdef CONFIG_SENSORS_LTC1710
+	sensors_ltc1710_init();
+#endif
+	return 0;
+}
+
+#endif				/* MODULE */
Index: /lm-sensors/tags/V2-5-5/README.patches
===================================================================
--- /lm-sensors/tags/V2-5-5/README.patches	(revision 738)
+++ /lm-sensors/tags/V2-5-5/README.patches	(revision 738)
@@ -0,0 +1,30 @@
+These patches were pre-made using the i2c and lm_sensors packages.
+
+In this document, the example version used of this patch is 
+lm_sensors-2.4.0-i2c-kernel-2.2.12.  Your actual version may be
+newer than this.
+
+To use:
+
+* Download a virgin kernel distribution of the exact version as
+  this patch distribution.
+
+* Unpackage and install the kernel source, but do not configure
+  or 'make' it yet.
+
+* Apply the I2C patch, for example:
+
+# patch -d /usr/src/linux -p1 < lm_sensors-2.4.0-i2c-kernel-2.2.12.patch 
+
+* Apply the Lm_sensors patch, for example:
+
+# patch -d /usr/src/linux -p1 < lm_sensors-2.4.0-lm-kernel-2.2.12.patch
+
+
+The patches must be applied in this order: i2c then lm_sensors
+
+
+Enjoy!
+
+
+-- The Lm_sensors Development Team <sensors@stimpy.netroedge.com>
Index: /lm-sensors/tags/V2-5-5/mkpatch/mkpatch.pl
===================================================================
--- /lm-sensors/tags/V2-5-5/mkpatch/mkpatch.pl	(revision 992)
+++ /lm-sensors/tags/V2-5-5/mkpatch/mkpatch.pl	(revision 992)
@@ -0,0 +1,1149 @@
+#!/usr/bin/perl
+
+#    mkpatch - Create patches against the Linux kernel
+#    Copyright (c) 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.
+
+use strict;
+
+use vars qw($temp);
+$temp = "mkpatch/.temp";
+
+# Generate a diff between the old kernel file and the new lm_sensors file. We
+# arrange the headers to tell us the old tree was under directory
+# `linux-old', and the new tree under `linux'.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+# $_[2]: Name of the kernel file
+# $_[3]: Name of the patched file
+sub print_diff
+{
+  my ($package_root,$kernel_root,$kernel_file,$package_file) = @_;
+  my ($diff_command,$dummy);
+
+  $diff_command = "diff -u2";
+  if ( -e "$kernel_root/$kernel_file") {
+    $diff_command .= " $kernel_root/$kernel_file ";
+  } else {
+    $diff_command .= " /dev/null ";
+  }
+  if ( -e "$package_root/$package_file") {
+    $diff_command .= " $package_root/$package_file ";
+  } else {
+    $diff_command .= " /dev/null";
+  }
+  open INPUT, "$diff_command|" or die "Can't execute `$diff_command'";
+  $dummy = <INPUT>;
+  $dummy = <INPUT>;
+  print "--- linux-old/$kernel_file\t".`date`;
+  print "+++ linux/$kernel_file\t".`date`;
+    
+  while (<INPUT>) {
+    print;
+  }
+  close INPUT;
+}
+
+# This generates diffs for kernel file Documentation/Configure.help. This
+# file contains the help texts that can be displayed during `make *config'
+# for the kernel.
+# The new texts are put at the end of the file, or just before the
+# lm_sensors texts.
+# Of course, care is taken old lines are removed.
+# $_[0]: i2c package root (like /tmp/i2c)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_Documentation_Configure_help
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "Documentation/Configure.help";
+  my $package_file = $temp;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  MAIN: while(<INPUT>) {
+    if (m@I2C mainboard interfaces@ or 
+           m@Acer Labs ALI 1533 and 1543C@ or
+           m@Apple Hydra Mac I/O@ or
+           m@Intel 82371AB PIIX4\(E\)@ or
+           m@VIA Technologies, Inc. VT82C586B@ or
+           m@Pseudo ISA adapter \(for hardware sensors modules\)@ or
+           m@Analog Devices ADM1021 and compatibles@ or
+           m@Analog Devices ADM9240 and compatibles@ or
+           m@Genesys Logic GL518SM@ or
+           m@National Semiconductors LM75@ or
+           m@National Semiconductors LM78@ or
+           m@National Semiconductors LM80@ or
+           m@Silicon Integrated Systems Corp. SiS5595@ or
+           m@Winbond W83781D, W83782D and W83783S@ or
+           m@EEprom \(DIMM\) reader@ or
+           m@Linear Technologies LTC1710@) {
+      $_ = <INPUT>;
+      $_ = <INPUT>;
+      $_ = <INPUT> while not m@^\S@ and not eof(INPUT);
+      redo MAIN;
+    }
+    if (eof(INPUT)) {
+      print OUTPUT <<'EOF'
+I2C mainboard interfaces
+CONFIG_I2C_MAINBOARD
+  Many modern mainboards have some kind of I2C interface integrated. This
+  is often in the form of a SMBus, or System Management Bus, which is
+  basically the same as I2C but which uses only a subset of the I2C
+  protocol.
+
+  You will also want the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Acer Labs ALI 1533 and 1543C
+CONFIG_I2C_ALI15X3
+  If you say yes to this option, support will be included for the Acer
+  Labs ALI 1533 and 1543C mainboard I2C interfaces. This can also be 
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Apple Hydra Mac I/O
+CONFIG_I2C_HYDRA
+  If you say yes to this option, support will be included for the 
+  Hydra mainboard I2C interface. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+Intel 82371AB PIIX4(E)
+CONFIG_I2C_PIIX4
+  If you say yes to this option, support will be included for the 
+  Intel PIIX4 and PIIX4E mainboard I2C interfaces. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+VIA Technologies, Inc. VT82C586B
+CONFIG_I2C_VIA
+  If you say yes to this option, support will be included for the VIA
+  Technologies I2C adapter found on some motherboards. This can also 
+  be built as a module which can be inserted and removed while the 
+  kernel is running.
+
+Pseudo ISA adapter (for hardware sensors modules)
+CONFIG_I2C_ISA
+  This provides support for accessing some hardware sensor chips over
+  the ISA bus rather than the I2C or SMBus. If you want to do this, 
+  say yes here. This feature can also be built as a module which can 
+  be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM1021 and compatibles
+CONFIG_SENSORS_ADM1021 
+  If you say yes here you get support for Analog Devices ADM1021 
+  sensor chips and clones: the Maxim MAX1617 and MAX1617A, the
+  TI THMC10 and the XEON processor built-in sensor. This can also 
+  be built as a module which can be inserted and removed while the 
+  kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM9240 and compatibles
+CONFIG_SENSORS_ADM9240
+  If you say yes here you get support for Analog Devices ADM9240 
+  sensor chips and clones: the Dallas Semiconductors DS1780 and
+  the National Semiconductors LM81. This can also be built as a 
+  module which can be inserted and removed while the kernel is
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Genesys Logic GL518SM
+CONFIG_SENSORS_GL518SM
+  If you say yes here you get support for Genesys Logic GL518SM sensor
+  chips.  This can also be built as a module which can be inserted and
+  removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductors LM75
+CONFIG_SENSORS_LM75 
+  If you say yes here you get support for National Semiconductor LM75
+  sensor chips. This can also be built as a module which can be
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductors LM78
+CONFIG_SENSORS_LM78
+  If you say yes here you get support for National Semiconductor LM78
+  sensor chips family: the LM78-J and LM79. Many clone chips will
+  also work at least somewhat with this driver. This can also be built
+  as a module which can be inserted and removed while the kernel is 
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductors LM80
+CONFIG_SENSORS_LM80
+  If you say yes here you get support for National Semiconductor LM80
+  sensor chips. This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Silicon Integrated Systems Corp. SiS5595
+CONFIG_SENSORS_SIS5595
+  If you say yes here you get support for Silicon Integrated Systems 
+  Corp.  SiS5595 sensor chips. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Winbond W83781D, W83782D and W83783S
+CONFIG_SENSORS_W83781D
+  If you say yes here you get support for the Winbond W8378x series 
+  of sensor chips: the W83781D, W83782D, W83783S and W83682HF. This 
+  can also be built as a module which can be inserted and removed
+  while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+EEprom (DIMM) reader
+CONFIG_SENSORS_EEPROM
+  If you say yes here you get read-only access to the EEPROM data 
+  available on modern memory DIMMs, and which could theoretically
+  also be available on other devices. This can also be built as a 
+  module which can be inserted and removed while the kernel is 
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Linear Technologies LTC1710
+CONFIG_SENSORS_LTC1710
+  If you say yes here you get support for Linear Technologies LTC1710
+  sensor chips. This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+EOF
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+
+# This generates diffs for the main Linux Makefile.
+# Three lines which add drivers/sensors/sensors.a to the DRIVERS list are 
+# put just before the place where the architecture Makefile is included.
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_Makefile
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "Makefile";
+  my $package_file = $temp;
+  my $type = 0;
+  my $pr1 = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  `grep -q -s 'i2c\.o' "$kernel_root/$kernel_file"`;
+  $type = 2 if ! $?;
+  MAIN: while(<INPUT>) {
+    $type = 1 if !$type and (m@^DRIVERS-\$@);
+    if (m@DRIVERS-\$\(CONFIG_SENSORS\)@) {
+      $_ = <INPUT>;
+      redo MAIN;
+    } elsif (m@CONFIG_SENSORS@) {
+      $_ = <INPUT> while not m@endif@;
+      $_ = <INPUT>;
+      $_ = <INPUT> if m@^$@;
+      redo MAIN;
+    }
+    if ($type == 1 and m@^DRIVERS \+= \$\(DRIVERS-y\)@) {
+      print OUTPUT <<'EOF';
+DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensors.a
+EOF
+      $pr1 = 1;
+    }
+    if ($type == 2 and m@^DRIVERS \+= \$\(DRIVERS-y\)@) {
+      print OUTPUT <<'EOF';
+DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensor.o
+EOF
+      $pr1 = 1;
+    }
+    if ($type == 0 and m@include arch/\$\(ARCH\)/Makefile@) {
+      print OUTPUT <<'EOF';
+ifeq ($(CONFIG_SENSORS),y)
+DRIVERS := $(DRIVERS) drivers/sensors/sensors.a
+endif
+
+EOF
+      $pr1 = 1;
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `Makefile' failed.\n".
+      "Contact the authors please!" if $pr1 == 0;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+# This generates diffs for drivers/Makefile
+# First, `sensors' is added to the ALL_SUB_DIRS list. Next, a couple of lines
+# to add sensors to the SUB_DIRS and/or MOD_SUB_DIRS lists is put right before
+# Rules.make is included.
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_Makefile
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/Makefile";
+  my $package_file = $temp;
+  my $sensors_present;
+  my $pr1 = 0;
+  my $pr2 = 0;
+  my $new_style = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  MAIN: while(<INPUT>) {
+    if (m@^mod-subdirs\s*:=@) {
+      $new_style = 1;
+    }
+    if ((! $new_style and m@^ALL_SUB_DIRS\s*:=@) or m@^mod-subdirs\s*:=@) {
+      $pr1 = 1;
+      $sensors_present = 0;
+      while (m@\\$@) {
+        $sensors_present = 1 if m@sensors@;
+        print OUTPUT;
+        $_ = <INPUT>;
+      }
+      $sensors_present = 1 if m@sensors@;
+      s@$@ sensors@ if (not $sensors_present);
+      print OUTPUT;
+      $_ = <INPUT>;
+      redo MAIN;
+    } 
+    if (m@^ifeq.*CONFIG_SENSORS@) {
+      $_ = <INPUT> while not m@^endif@;
+      $_ = <INPUT>;
+      $_ = <INPUT> if m@^$@;
+      redo MAIN;
+    } 
+    if (m@^subdir.*CONFIG_SENSORS@) {
+      $_ = <INPUT>;
+      redo MAIN;
+    }
+    if (!$pr2 and (m@^include \$\(TOPDIR\)/Rules.make$@ or m@^subdir-\$\(CONFIG_ACPI@)) {
+      $pr2 = 1;
+      if ($new_style) {
+      print OUTPUT <<'EOF';
+subdir-$(CONFIG_SENSORS) 	+= sensors
+EOF
+      } else {
+      print OUTPUT <<'EOF';
+ifeq ($(CONFIG_SENSORS),y)
+SUB_DIRS += sensors
+MOD_SUB_DIRS += sensors
+else
+  ifeq ($(CONFIG_SENSORS),m)
+  MOD_SUB_DIRS += sensors
+  endif
+endif
+
+EOF
+      }
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/Makefile' failed.\n".
+      "Contact the authors please!" if $pr1 == 0 or $pr2 == 0;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+# This generates diffs for drivers/char/Config.in
+# It adds a line just before CONFIG_APM or main_menu_option lines to include
+# the sensors Config.in.
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_char_Config_in
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/char/Config.in";
+  my $package_file = $temp;
+  my $pr1 = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  MAIN: while(<INPUT>) {
+    if (m@source drivers/i2c/Config.in@) {
+      $pr1 = 1;
+      print OUTPUT;
+      print OUTPUT "\nsource drivers/sensors/Config.in\n";
+      $_ = <INPUT>;
+      redo MAIN;
+    }
+    if (m@sensors@) {
+      $_ = <INPUT>;
+      $_ = <INPUT> if m@^$@;
+      redo MAIN;
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/Makefile' failed.\n".
+      "Contact the authors please!" if $pr1 == 0;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+ 
+
+# This generates diffs for drivers/char/mem.c They are a bit intricate.
+# Lines are generated at the beginning to declare sensors_init_all
+# At the bottom, a call to sensors_init_all is added when the
+# new lm_sensors stuff is configured in.
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_char_mem_c
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/char/mem.c";
+  my $package_file = $temp;
+  my $right_place = 0;
+  my $done = 0;
+  my $atstart = 1;
+  my $pr1 = 0;
+  my $pr2 = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  MAIN: while(<INPUT>) {
+    if ($atstart and m@#ifdef@) {
+      $pr1 = 1;
+      print OUTPUT << 'EOF';
+#ifdef CONFIG_SENSORS
+extern void sensors_init_all(void);
+#endif
+EOF
+      $atstart = 0;
+    }
+    if (not $right_place and m@CONFIG_SENSORS@) {
+      $_ = <INPUT> while not m@#endif@;
+      $_ = <INPUT>;
+      redo MAIN;
+    }
+    $right_place = 1 if (m@lp_init\(\);@);
+    if ($right_place and not $done and
+        m@CONFIG_SENSORS@) {
+      $_ = <INPUT> while not m@#endif@;
+      $_ = <INPUT>;
+      $_ = <INPUT> if m@^$@;
+      redo MAIN;
+    }
+    if ($right_place and not $done and m@return 0;@) {
+      $pr2 = 1;
+      print OUTPUT <<'EOF';
+#ifdef CONFIG_SENSORS
+	sensors_init_all();
+#endif
+
+EOF
+      $done = 1;
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/char/mem.c' failed.\n".
+      "Contact the authors please!" if $pr1 == 0 or $pr2 == 0;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+
+# This generates diffs for drivers/i2c/Config.in
+# Several adapter drivers that are included in the lm_sensors package are
+# added at the first and onlu sensors marker.
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_i2c_Config_in
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/i2c/Config.in";
+  my $package_file = "$temp";
+  my $pr1 = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  while(<INPUT>) {
+    if (m@sensors code starts here@) {
+      $pr1++;
+      print OUTPUT;
+      while (<INPUT>) {
+        last if m@sensors code ends here@;
+      }
+      print OUTPUT << 'EOF';
+  bool 'I2C mainboard interfaces' CONFIG_I2C_MAINBOARD 
+  if [ "$CONFIG_I2C_MAINBOARD" = "y" ]; then
+    tristate '  Acer Labs ALI 1533 and 1543C' CONFIG_I2C_ALI15X3 
+    dep_tristate '  Apple Hydra Mac I/O' CONFIG_I2C_HYDRA $CONFIG_I2C_ALGOBIT
+    tristate '  AMD 756' CONFIG_I2C_AMD756
+    tristate '  Intel 82801AA, 82801AB and 82801BA' CONFIG_I2C_I801
+    tristate '  Intel i810AA, i810AB and i815' CONFIG_I2C_I810
+    tristate '  Intel 82371AB PIIX4(E)' CONFIG_I2C_PIIX4
+    dep_tristate '  VIA Technologies, Inc. VT82C586B' CONFIG_I2C_VIA $CONFIG_I2C_ALGOBIT
+    tristate '  VIA Technologies, Inc. VT596A/B' CONFIG_I2C_VIAPRO
+    dep_tristate '  Voodoo3 I2C interface' CONFIG_I2C_VOODOO $CONFIG_I2C_ALGOBIT
+    tristate '  Pseudo ISA adapter (for hardware sensors modules)' CONFIG_I2C_ISA 
+  fi
+
+EOF
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/i2c/Config.in' failed.\n".
+      "Contact the authors please!" if $pr1 != 1;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+sub gen_drivers_sensors_Makefile
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/sensors/Makefile";
+  my $package_file = $temp;
+  my $use_new_format;
+  `grep -q -s 'i2c\.o' "$kernel_root/drivers/i2c/Makefile"`;
+  $use_new_format = ! $?;
+
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  if ($use_new_format) {
+    print OUTPUT <<'EOF';
+#
+# Makefile for the kernel hardware sensors drivers.
+#
+
+MOD_LIST_NAME := SENSORS_MODULES
+O_TARGET := sensor.o
+
+export-objs	:= sensors.o
+
+obj-$(CONFIG_SENSORS)		+= sensors.o
+obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
+obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025.o
+obj-$(CONFIG_SENSORS_ADM9024)	+= adm9024.o
+obj-$(CONFIG_SENSORS_BT869)	+= bt869.o
+obj-$(CONFIG_SENSORS_DDCMON)	+= ddcmon.o
+obj-$(CONFIG_SENSORS_EEPROM)	+= eeprom.o
+obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
+obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
+obj-$(CONFIG_SENSORS_LM75)	+= lm75.o
+obj-$(CONFIG_SENSORS_LM78)	+= lm78.o
+obj-$(CONFIG_SENSORS_LM80)	+= lm80.o
+obj-$(CONFIG_SENSORS_LM87)	+= lm87.o
+obj-$(CONFIG_SENSORS_LTC1710)	+= ltc1710.o
+obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595.o
+obj-$(CONFIG_SENSORS_THMC50)	+= thmc50.o
+obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
+obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
+
+include $(TOPDIR)/Rules.make
+
+EOF
+  } else {
+    print OUTPUT <<'EOF';
+#
+# Makefile for the kernel hardware sensors drivers.
+#
+
+SUB_DIRS     :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+MOD_LIST_NAME := SENSORS_MODULES
+
+L_TARGET := sensors.a
+MX_OBJS :=  
+M_OBJS  := 
+LX_OBJS :=
+L_OBJS  := 
+
+# -----
+# i2c core components
+# -----
+
+ifeq ($(CONFIG_SENSORS),y)
+  LX_OBJS += sensors.o
+else
+  ifeq ($(CONFIG_SENSORS),m)
+    MX_OBJS += sensors.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_ADM1021),y)
+  L_OBJS += adm1021.o
+else
+  ifeq ($(CONFIG_SENSORS_ADM1021),m)
+    M_OBJS += adm1021.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_ADM1025),y)
+  L_OBJS += adm1025.o
+else
+  ifeq ($(CONFIG_SENSORS_ADM1025),m)
+    M_OBJS += adm1025.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_ADM9024),y)
+  L_OBJS += adm9240.o
+else
+  ifeq ($(CONFIG_SENSORS_ADM9024),m)
+    M_OBJS += adm9240.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_DDCMON),y)
+  L_OBJS += ddcmon.o
+else
+  ifeq ($(CONFIG_SENSORS_DDCMON),m)
+    M_OBJS += ddcmon.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_EEPROM),y)
+  L_OBJS += eeprom.o
+else
+  ifeq ($(CONFIG_SENSORS_EEPROM),m)
+    M_OBJS += eeprom.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_GL518SM),y)
+  L_OBJS += gl518sm.o
+else
+  ifeq ($(CONFIG_SENSORS_GL518SM),m)
+    M_OBJS += gl518sm.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_GL520SM),y)
+  L_OBJS += gl520sm.o
+else
+  ifeq ($(CONFIG_SENSORS_GL520SM),m)
+    M_OBJS += gl520sm.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_LM75),y)
+  L_OBJS += lm75.o
+else
+  ifeq ($(CONFIG_SENSORS_LM75),m)
+    M_OBJS += lm75.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_LM78),y)
+  L_OBJS += lm78.o
+else
+  ifeq ($(CONFIG_SENSORS_LM78),m)
+    M_OBJS += lm78.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_LM80),y)
+  L_OBJS += lm80.o
+else
+  ifeq ($(CONFIG_SENSORS_LM80),m)
+    M_OBJS += lm80.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_LM87),y)
+  L_OBJS += lm87.o
+else
+  ifeq ($(CONFIG_SENSORS_LM87),m)
+    M_OBJS += lm87.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_LTC1710),y)
+  L_OBJS += ltc1710.o
+else
+  ifeq ($(CONFIG_SENSORS_LTC1710),m)
+    M_OBJS += ltc1710.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_MATORB),y)
+  L_OBJS += matorb.o
+else
+  ifeq ($(CONFIG_SENSORS_MATORB),m)
+    M_OBJS += matorb.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_SIS5595),y)
+  L_OBJS += sis5595.o
+else
+  ifeq ($(CONFIG_SENSORS_SIS5595),m)
+    M_OBJS += sis5595.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_THMC50),y)
+  L_OBJS += thmc50.o
+else
+  ifeq ($(CONFIG_SENSORS_THMC50),m)
+    M_OBJS += thmc50.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_VIA686A),y)
+  L_OBJS += via686a.o
+else
+  ifeq ($(CONFIG_SENSORS_VIA686A),m)
+    M_OBJS += via686a.o
+  endif
+endif
+
+ifeq ($(CONFIG_SENSORS_W83781D),y)
+  L_OBJS += w83781d.o
+else
+  ifeq ($(CONFIG_SENSORS_W83781D),m)
+    M_OBJS += w83781d.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+EOF
+  }  
+  close OUTPUT;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+# This generates diffs for drivers/i2c/Makefile.
+# Lines to add correct files to M_OBJS and/or L_OBJS are added just before
+# Rules.make is included
+# Of course, care is taken old lines are removed.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_i2c_Makefile
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/i2c/Makefile";
+  my $package_file = $temp;
+  my $pr1 = 0;
+  my $new_format = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  while(<INPUT>) {
+    $new_format = 1 if m@i2c\.o@;
+    if (m@sensors code starts here@) {
+      $pr1 ++;
+      print OUTPUT;
+      while (<INPUT>) {
+        last if m@sensors code ends here@;
+      }
+      if ($new_format) {
+        print OUTPUT << 'EOF';
+obj-$(CONFIG_I2C_ALI15X3)		+= i2c-ali15x3.o
+obj-$(CONFIG_I2C_AMD756)		+= i2c-amd756.o
+obj-$(CONFIG_I2C_HYDRA)			+= i2c-hydra.o
+obj-$(CONFIG_I2C_I801)			+= i2c-i801.o
+obj-$(CONFIG_I2C_I810)			+= i2c-i810.o
+obj-$(CONFIG_I2C_ISA)			+= i2c-isa.o
+obj-$(CONFIG_I2C_PIIX4)			+= i2c-piix4.o
+obj-$(CONFIG_I2C_SIS5595)		+= i2c-sis5595.o
+obj-$(CONFIG_I2C_VIA)			+= i2c-via.o
+obj-$(CONFIG_I2C_VIAPRO)		+= i2c-viapro.o
+obj-$(CONFIG_I2C_VOODOO3)		+= i2c-voodoo3.o
+EOF
+      } else {
+        print OUTPUT << 'EOF';
+ifeq ($(CONFIG_I2C_ALI15X3),y)
+  L_OBJS += i2c-ali15x3.o
+else 
+  ifeq ($(CONFIG_I2C_ALI15X3),m)
+    M_OBJS += i2c-ali15x3.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_AMD756),y)
+  L_OBJS += i2c-amd756.o
+else 
+  ifeq ($(CONFIG_I2C_AMD756),m)
+    M_OBJS += i2c-amd756.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_HYDRA),y)
+  L_OBJS += i2c-hydra.o
+else 
+  ifeq ($(CONFIG_I2C_HYDRA),m)
+    M_OBJS += i2c-hydra.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_I801),y)
+  L_OBJS += i2c-i801.o
+else 
+  ifeq ($(CONFIG_I2C_I801),m)
+    M_OBJS += i2c-i801.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_I810),y)
+  L_OBJS += i2c-i810.o
+else 
+  ifeq ($(CONFIG_I2C_I810),m)
+    M_OBJS += i2c-i810.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_ISA),y)
+  L_OBJS += i2c-isa.o
+else 
+  ifeq ($(CONFIG_I2C_ISA),m)
+    M_OBJS += i2c-isa.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_PIIX4),y)
+  L_OBJS += i2c-piix4.o
+else 
+  ifeq ($(CONFIG_I2C_PIIX4),m)
+    M_OBJS += i2c-piix4.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_SIS5595),y)
+  L_OBJS += i2c-sis5595.o
+else 
+  ifeq ($(CONFIG_I2C_SIS5595),m)
+    M_OBJS += i2c-sis5595.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_VIA),y)
+  L_OBJS += i2c-via.o
+else 
+  ifeq ($(CONFIG_I2C_VIA),m)
+    M_OBJS += i2c-via.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_VIAPRO),y)
+  L_OBJS += i2c-viapro.o
+else 
+  ifeq ($(CONFIG_I2C_VIAPRO),m)
+    M_OBJS += i2c-viapro.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2C_VOODOO3),y)
+  L_OBJS += i2c-voodoo3.o
+else 
+  ifeq ($(CONFIG_I2C_VOODOO3),m)
+    M_OBJS += i2c-voodoo3.o
+  endif
+endif
+
+EOF
+      }
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/i2c/Makefile' failed.\n".
+      "Contact the authors please!" if $pr1 != 1;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+# This generates diffs for drivers/i2c/i2c-core.c
+# Lines are generated at the beginning to declare several *_init functions.
+# At the bottom, calls to them are added when the sensors stuff is configured
+# in.
+# $_[0]: sensors package root (like /tmp/sensors)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_drivers_i2c_i2c_core_c
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "drivers/i2c/i2c-core.c";
+  my $package_file = $temp;
+  my $patch_nr = 1;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  while(<INPUT>) {
+    if (m@sensors code starts here@) {
+      print OUTPUT;
+      while (<INPUT>) {
+        last if m@sensors code ends here@;
+      }
+      if ($patch_nr == 1) {
+        print OUTPUT << 'EOF';
+#ifdef CONFIG_I2C_ALI15X3
+	extern int i2c_ali15x3_init(void);
+#endif
+#ifdef CONFIG_I2C_AMD756
+	extern int i2c_amd756_init(void);
+#endif
+#ifdef CONFIG_I2C_HYDRA
+	extern int i2c_hydra_init(void);
+#endif
+#ifdef CONFIG_I2C_I801
+	extern int i2c_i801_init(void);
+#endif
+#ifdef CONFIG_I2C_I810
+	extern int i2c_i810_init(void);
+#endif
+#ifdef CONFIG_I2C_ISA
+	extern int i2c_isa_init(void);
+#endif
+#ifdef CONFIG_I2C_PIIX4
+	extern int i2c_piix4_init(void);
+#endif
+#ifdef CONFIG_I2C_SIS5595
+	extern int i2c_sis5595_init(void);
+#endif
+#ifdef CONFIG_I2C_VIA
+	extern int i2c_via_init(void);
+#endif
+#ifdef CONFIG_I2C_VIAPRO
+	extern int i2c_vt596_init(void);
+#endif
+#ifdef CONFIG_I2C_VOODOO3
+	extern int i2c_voodoo3_init(void);
+#endif
+EOF
+      } elsif ($patch_nr == 2) {
+      print OUTPUT << 'EOF';
+#ifdef CONFIG_I2C_ALI15X3
+	i2c_ali15x3_init();
+#endif
+#ifdef CONFIG_I2C_AMD756
+	i2c_amd756_init();
+#endif
+#ifdef CONFIG_I2C_HYDRA
+	i2c_hydra_init();
+#endif
+#ifdef CONFIG_I2C_I801
+	i2c_i801_init();
+#endif
+#ifdef CONFIG_I2C_I810
+	i2c_i810_init();
+#endif
+#ifdef CONFIG_I2C_PIIX4
+	i2c_piix4_init();
+#endif
+#ifdef CONFIG_I2C_SIS5595
+	i2c_sis5595_init();
+#endif
+#ifdef CONFIG_I2C_VIA
+	i2c_via_init();
+#endif
+#ifdef CONFIG_I2C_VIAPRO
+	i2c_vt596_init();
+#endif
+#ifdef CONFIG_I2C_VOODOO3
+	i2c_voodoo3_init();
+#endif
+#ifdef CONFIG_I2C_ISA
+	i2c_isa_init();
+#endif
+EOF
+      }
+      $patch_nr ++;
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `drivers/i2c/i2c-core.c' failed.\n".
+      "Contact the authors please!" if $patch_nr != 3;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+# Generate the diffs for the list of MAINTAINERS
+# $_[0]: i2c package root (like /tmp/i2c)
+# $_[1]: Linux kernel tree (like /usr/src/linux)
+sub gen_MAINTAINERS
+{
+  my ($package_root,$kernel_root) = @_;
+  my $kernel_file = "MAINTAINERS";
+  my $package_file = $temp;
+  my $done = 0;
+
+  open INPUT,"$kernel_root/$kernel_file"
+        or die "Can't open `$kernel_root/$kernel_file'";
+  open OUTPUT,">$package_root/$package_file"
+        or die "Can't open $package_root/$package_file";
+  MAIN: while(<INPUT>) {
+    if (m@SENSORS DRIVERS@) {
+       $_=<INPUT> while not m@^$@;
+       $_=<INPUT>;
+       redo MAIN;
+    }
+    if (not $done and (m@SGI VISUAL WORKSTATION 320 AND 540@)) {
+      print OUTPUT <<'EOF';
+SENSORS DRIVERS
+P:      Frodo Looijaard
+M:      frodol@dds.nl
+P:      Philip Edelbrock
+M:      phil@netroedge.com
+L:      sensors@stimpy.netroedge.com
+W:      http://www.lm-sensors.nu/
+S:      Maintained
+
+EOF
+      $done = 1;
+    }
+    print OUTPUT;
+  }
+  close INPUT;
+  close OUTPUT;
+  die "Automatic patch generation for `MAINTAINERS' failed.\n".
+      "Contact the authors please!" if $done == 0;
+  print_diff $package_root,$kernel_root,$kernel_file,$package_file;
+}
+
+
+# Main function
+sub main
+{
+  my ($package_root,$kernel_root,%files,%includes,$package_file,$kernel_file);
+  my ($diff_command,$dummy,$data0,$data1,$sedscript,$version_string);
+
+  # --> Read the command-lineo
+  $package_root = $ARGV[0];
+  die "Package root `$package_root' is not found\n" 
+        unless -d "$package_root/mkpatch";
+  $kernel_root = $ARGV[1];
+  die "Kernel root `$kernel_root' is not found\n" 
+        unless -f "$kernel_root/Rules.make";
+
+  # --> Read FILES
+  open INPUT, "$package_root/mkpatch/FILES" 
+        or die "Can't open `$package_root/mkpatch/FILES'";
+  while (<INPUT>) {
+    ($data0,$data1) = /(\S+)\s+(\S+)/;
+    $files{$data0} = $data1;
+  } 
+  close INPUT;
+
+  # --> Read INCLUDES
+  open INPUT, "$package_root/mkpatch/INCLUDES" 
+        or die "Can't open `$package_root/mkpatch/INCLUDES'";
+  while (<INPUT>) {
+    ($data0,$data1) = /(\S+)\s+(\S+)/;
+    $includes{$data0} = $data1;
+    $sedscript .= 's,(#\s*include\s*)'.$data0.'(\s*),\1'."$data1".'\2, ; ';
+  } 
+  close INPUT;
+
+  die "First apply the i2c patches to `$kernel_root'!" 
+       if ! -d "$kernel_root/drivers/i2c";
+
+  # --> Read "version.h"
+  open INPUT, "$package_root/version.h"
+        or die "Can't open `$package_root/version.h'";
+  $version_string .= $_ while <INPUT>;
+  close INPUT;
+ 
+  # --> Start generating
+  foreach $package_file (sort keys %files) {
+    open INPUT,"$package_root/$package_file" 
+          or die "Can't open `$package_root/$package_file'";
+    open OUTPUT,">$package_root/$temp"
+          or die "Can't open `$package_root/$temp'";
+    while (<INPUT>) {
+      eval $sedscript;
+      if (m@#\s*include\s*"version.h"@) {
+        print OUTPUT $version_string;
+      } else {
+        print OUTPUT;
+      }
+    }
+    close INPUT;
+    close OUTPUT;
+
+    $kernel_file = $files{$package_file};
+    print_diff $package_root,$kernel_root,$kernel_file,$temp;
+  }
+
+  gen_Makefile $package_root, $kernel_root;
+  gen_drivers_Makefile $package_root, $kernel_root;
+  gen_drivers_sensors_Makefile $package_root, $kernel_root;
+  gen_drivers_char_Config_in $package_root, $kernel_root;
+  gen_drivers_char_mem_c $package_root, $kernel_root;
+  gen_drivers_i2c_Config_in $package_root, $kernel_root;
+  gen_drivers_i2c_Makefile $package_root, $kernel_root;
+  gen_drivers_i2c_i2c_core_c $package_root, $kernel_root;
+  gen_Documentation_Configure_help $package_root, $kernel_root;
+  gen_MAINTAINERS $package_root, $kernel_root;
+}
+
+main;
+
Index: /lm-sensors/tags/V2-5-5/mkpatch/FILES
===================================================================
--- /lm-sensors/tags/V2-5-5/mkpatch/FILES	(revision 962)
+++ /lm-sensors/tags/V2-5-5/mkpatch/FILES	(revision 962)
@@ -0,0 +1,33 @@
+kernel/busses/i2c-ali15x3.c	drivers/i2c/i2c-ali15x3.c
+kernel/busses/i2c-amd756.c	drivers/i2c/i2c-amd756.c
+kernel/busses/i2c-hydra.c	drivers/i2c/i2c-hydra.c
+kernel/busses/i2c-i801.c	drivers/i2c/i2c-i801.c
+kernel/busses/i2c-i810.c	drivers/i2c/i2c-i810.c
+kernel/busses/i2c-isa.c		drivers/i2c/i2c-isa.c
+kernel/busses/i2c-piix4.c	drivers/i2c/i2c-piix4.c
+kernel/busses/i2c-sis5595.c	drivers/i2c/i2c-sis5595.c
+kernel/busses/i2c-via.c		drivers/i2c/i2c-via.c
+kernel/busses/i2c-viapro.c	drivers/i2c/i2c-viapro.c
+kernel/busses/i2c-voodoo3.c	drivers/i2c/i2c-voodoo3.c
+kernel/sensors.c		drivers/sensors/sensors.c
+kernel/chips/adm1021.c		drivers/sensors/adm1021.c
+kernel/chips/adm1025.c		drivers/sensors/adm1025.c
+kernel/chips/adm9240.c		drivers/sensors/adm9240.c
+kernel/chips/bt869.c		drivers/sensors/bt869.c
+kernel/chips/ddcmon.c		drivers/sensors/ddcmon.c
+kernel/chips/eeprom.c		drivers/sensors/eeprom.c
+kernel/chips/gl518sm.c		drivers/sensors/gl518sm.c
+kernel/chips/gl520sm.c		drivers/sensors/gl520sm.c
+kernel/chips/lm75.c		drivers/sensors/lm75.c
+kernel/chips/lm78.c		drivers/sensors/lm78.c
+kernel/chips/lm80.c		drivers/sensors/lm80.c
+kernel/chips/lm87.c		drivers/sensors/lm87.c
+kernel/chips/ltc1710.c		drivers/sensors/ltc1710.c
+kernel/chips/matorb.c		drivers/sensors/matorb.c
+kernel/chips/sis5595.c		drivers/sensors/sis5595.c
+kernel/chips/thmc50.c		drivers/sensors/thmc50.c
+kernel/chips/via686a.c		drivers/sensors/via686a.c
+kernel/chips/w83781d.c		drivers/sensors/w83781d.c
+kernel/include/i2c-isa.h	include/linux/i2c-isa.h
+kernel/include/sensors.h	include/linux/sensors.h
+mkpatch/Config.in		drivers/sensors/Config.in
Index: /lm-sensors/tags/V2-5-5/mkpatch/INCLUDES
===================================================================
--- /lm-sensors/tags/V2-5-5/mkpatch/INCLUDES	(revision 514)
+++ /lm-sensors/tags/V2-5-5/mkpatch/INCLUDES	(revision 514)
@@ -0,0 +1,2 @@
+"i2c-isa.h"		<linux/i2c-isa.h>
+"sensors.h"		<linux/sensors.h>
Index: /lm-sensors/tags/V2-5-5/mkpatch/Config.in
===================================================================
--- /lm-sensors/tags/V2-5-5/mkpatch/Config.in	(revision 961)
+++ /lm-sensors/tags/V2-5-5/mkpatch/Config.in	(revision 961)
@@ -0,0 +1,36 @@
+#
+# Character device configuration
+#
+
+if [ "$CONFIG_I2C" = "m" -o "$CONFIG_I2C" = "y" ] ; then
+  mainmenu_option next_comment
+  comment 'Hardware sensors support'
+  
+  dep_tristate 'Hardware sensors support' CONFIG_SENSORS $CONFIG_I2C
+  
+  if [ "$CONFIG_SENSORS" != "n" ]; then
+    dep_tristate '  Analog Devices ADM1021 and compatibles' CONFIG_SENSORS_ADM1021 $CONFIG_SENSORS
+    dep_tristate '  Analog Devices ADM1025' CONFIG_SENSORS_ADM1025 $CONFIG_SENSORS
+    dep_tristate '  Analog Devices ADM9240 and compatibles' CONFIG_SENSORS_ADM9240 $CONFIG_SENSORS
+    dep_tristate '  Genesys Logic GL518SM' CONFIG_SENSORS_GL518SM $CONFIG_SENSORS
+    dep_tristate '  Genesys Logic GL520SM' CONFIG_SENSORS_GL520SM $CONFIG_SENSORS
+    dep_tristate '  National Semiconductors LM75' CONFIG_SENSORS_LM75 $CONFIG_SENSORS
+    dep_tristate '  National Semiconductors LM78' CONFIG_SENSORS_LM78 $CONFIG_SENSORS
+    dep_tristate '  National Semiconductors LM80' CONFIG_SENSORS_LM80 $CONFIG_SENSORS
+    dep_tristate '  National Semiconductors LM87' CONFIG_SENSORS_LM87 $CONFIG_SENSORS
+    dep_tristate '  Silicon Integrated Systems Corp. SiS5595' CONFIG_SENSORS_SIS5595 $CONFIG_SENSORS
+    dep_tristate '  Texas Instruments THMC50 and compatibles' CONFIG_THMC50 $CONFIG_SENSORS
+    dep_tristate '  VIA 686a Integrated Hardware Monitor' CONFIG_SENSORS_VIA686A $CONFIG_SENSORS
+    dep_tristate '  Winbond W83781D, W83782D and W83783S' CONFIG_SENSORS_W83781D $CONFIG_SENSORS
+    bool 'Other I2C devices' CONFIG_SENSORS_OTHER 
+    if [ "$CONFIG_SENSORS_OTHER" = "y" ] ; then
+      dep_tristate '  Brooktree BT869 Video Modulator' CONFIG_SENSORS_BT869 $CONFIG_SENSORS
+      dep_tristate '  DDC Monitor EDID EEPROM' CONFIG_SENSORS_DDCMON $CONFIG_SENSORS
+      dep_tristate '  EEprom (DIMM) reader ' CONFIG_SENSORS_EEPROM $CONFIG_SENSORS
+      dep_tristate '  Linear Technologies LTC1710 ' CONFIG_SENSORS_LTC1710 $CONFIG_SENSORS
+      dep_tristate '  Matrix-Orbital LCD Displays' CONFIG_SENSORS_MATORB $CONFIG_MATORB
+    fi
+  fi
+  endmenu
+fi
+
Index: /lm-sensors/tags/V2-5-5/mkpatch/Module.mk
===================================================================
--- /lm-sensors/tags/V2-5-5/mkpatch/Module.mk	(revision 581)
+++ /lm-sensors/tags/V2-5-5/mkpatch/Module.mk	(revision 581)
@@ -0,0 +1,26 @@
+#  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 := mkpatch
+MKPATCHDIR := $(MODULE_DIR)
+
+clean-mkpatch:
+	$(RM) $(MKPATCHDIR)/.temp
+clean :: clean-mkpatch
Index: /lm-sensors/tags/V2-5-5/README.package
===================================================================
--- /lm-sensors/tags/V2-5-5/README.package	(revision 738)
+++ /lm-sensors/tags/V2-5-5/README.package	(revision 738)
@@ -0,0 +1,32 @@
+This is a bundling of the i2c and lm_sensors packages for use in either
+patching against a kernel source tree or building externally to the
+kernel.
+
+QUICKSTART
+----------
+
+To patch against the kernel (as the proper user w/ permissions to alter
+the kernel source tree):
+
+# tar xzvf lm-sensors-x.x.x.tar.gz
+# cd lm-sensors-x.x.x
+# cd i2c
+[ Read COPYING, README, INSTALL, and other docs as needed ]
+# mkpatch/mkpatch.pl . /usr/src/linux | patch -p1 -d /usr/src/linux
+# cd ../lm_sensors2
+[ Read COPYING, README, INSTALL, and other docs as needed ]
+# mkpatch/mkpatch.pl . /usr/src/linux | patch -p1 -d /usr/src/linux
+# cd /usr/src/linux
+# make clean
+
+Then configure, build, and install your kernel as usual.
+
+You will notice the you have some new kernel configuration options to use.
+
+To build the package independently of the kernel, please read the README's
+and INSTALLs in i2c and lm_sensors2.
+
+
+Enjoy!
+
+-- The Lm_sensors Development Team <sensors@stimpy.netroedge.com>
Index: /lm-sensors/tags/V2-5-5/QUICKSTART
===================================================================
--- /lm-sensors/tags/V2-5-5/QUICKSTART	(revision 981)
+++ /lm-sensors/tags/V2-5-5/QUICKSTART	(revision 981)
@@ -0,0 +1,45 @@
+LM_SENSORS QUICK INSTALLATION INSTRUCTIONS
+------------------------------------------
+
+NOTE: PLEASE SKIM THE INSTALL DOCUMENT, OR BETTER YET READ IT CAREFULLY!
+The instructions in this document will generally work if you have a standard
+system, but not always!!
+
+Quickstart:
+
+  * Make sure you have the kernel tree corresponding to your current
+    kernel in /usr/src/linux. Distribution kernels usually won't do,
+    compile your own from a 'vanilla' tree (get one from
+    ftp://ftp.kernel/org/pub/linux/kernel/).
+
+  * Download, build, and install the latest i2c package.
+    This is not necessary if you are using lm_sensors 2.5.5
+    with a 2.4 kernel.
+
+  * Do `make'.
+    The warnings about .d files at the start are harmless.
+    If you have compile problems, you probably should not have skipped step 2.
+    Go back and install the latest i2c package.
+
+  * Do (as root) `make install'.
+
+  * Do (as root) `depmod -a'.
+
+  * Add  the line
+      /usr/local/lib 
+    to /etc/ld.so.conf if it is not there yet.
+
+  * Do (as root) `ldconfig'.
+
+  * Run (as root) `prog/mkdev/mkdev.sh' to create the device files.
+
+  * Run (as root) `prog/detect/sensors-detect' to detect your hardware,
+    put the lines it suggests into /etc/modules.conf
+    and enter the modprobe lines sensors-detect suggests.
+    Add the line `sensors -s' after the modprobe lines.
+
+  * Reboot, or else type in the modprobe lines and `sensors -s'.
+
+  * Run `sensors' to see your output.
+
+  * That's it! If you have problems, read the INSTALL document.
Index: /lm-sensors/tags/V2-5-5/CONTRIBUTORS
===================================================================
--- /lm-sensors/tags/V2-5-5/CONTRIBUTORS	(revision 909)
+++ /lm-sensors/tags/V2-5-5/CONTRIBUTORS	(revision 909)
@@ -0,0 +1,58 @@
+This package could not have been created without the help of many, many people.
+The most notable contributors are listed below. You are probably best off
+to mail <sensors@stimpy.netroedge.com> if you have questions, suggestions or
+problems.
+
+* Alexander Larsson <alla@lysator.liu.se>
+  Wrote the original lm78 module; lm_sensors version 1 was no more than an
+  extension of this module. He still likes to keep an eye on current
+  developments, but is usually too busy with other things to do much work
+  on them
+* Philip Edelbrock <phil@netroedge.com>
+  Took up the development of the Alex' lm78 module. Responsible for most
+  SMBus code, and many, many other things. He is still one of the main
+  developers of the current package.
+* Frodo Looijaard <frodol@dds.nl>
+  Took up the development of Alex' lm78 module. Large pieces of code were
+  written by him. Designed lm_sensors version 2, and wrote most of its
+  core code, including many drivers.
+* William Morgan <wmorgan@cmu.edu>
+  Helped with some patches in lm_sensors version 1. He provided us with
+  the original CVS archive, and even managed to explain to us how to use
+  it.
+* Kyösti Mälkki <kmalkki@cc.hut.fi>
+  Author of the i2c-via and i2c-viapro host drivers. Sis5595 hack.
+  Extended support for GL518SM and GL520SM sensors.
+* Mark D. Studebaker <mdsxyz123@yahoo.com>
+  Author of the i2c-ali15x3, i2c-i801, and i2c-i810 bus drivers.
+  Author (with Bob Dougherty) of the via686a driver.
+  Added w83782d, w83783s, w83627hf, and as99127f support to the w83781d driver.
+  Added max1617, thmc10, gl523sm, and lm84 support to the adm1021 driver.
+  Added lm81 support to the adm9240 driver.
+  Added DDC support and i2c-algo-bit layer to the i2c-voodoo3 bus driver.
+  Author of the decode-xeon.pl xeon EEPROM decoder.
+* Bob Dougherty <bobd@stanford.edu>
+  Author (with MDS) of the via686a driver.
+* Jonathan Teh Soon Yew <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>
+  Contributed conversion factor data for the via686a driver.
+* Geert Uytterhoeven <geert@linux-m68k.org>
+  Author of the i2c-hydra driver.
+* Adrian Baugh <adrian.baugh@keble.ox.ac.uk>
+  Author of wmsensors (get it at http://users.ox.ac.uk/~kebl0850/wmlm78);
+  he also tested lots of things, and helped us out on several occasions.
+* Fons Rademakers <Fons.Rademakers@cern.ch>
+  Author of the maxilife module (which could only be written under NDA)
+* Merlin Hughes <merlin@merlin.org>
+  Author of the AMD756 module and sensord.
+* Constantine Gavrilov <const-g@xpert.com>
+  Created and maintains the RPM files
+* Simon Vogl <simon@tk.uni-linz.ac.at>
+  Wrote the new i2c kernel driver. This driver is indispensable for the
+  workings of lm_sensors version 2.
+* Gerd Knorr <kraxel@cs.tu-berlin.de> (?)
+  Designed the i2c structs, which made it possible to use the smbus-is-
+  just-an-i2c-extension approach.
+* Chen-Yuan Wu <gwu@esoft.com>
+  Author of the adm1025 driver.
+* Kris Van Hees <aedil@alchar.org>
+  Author of the mtp008 chip driver.
Index: /lm-sensors/tags/V2-5-5/README
===================================================================
--- /lm-sensors/tags/V2-5-5/README	(revision 986)
+++ /lm-sensors/tags/V2-5-5/README	(revision 986)
@@ -0,0 +1,135 @@
+=============================================================================
+     FOR QUICK INSTALLATION INSTRUCTIONS SEE THE 'QUICKSTART' FILE.
+     FOR FULL INSTALLATION INSTRUCTIONS SEE THE 'INSTALL' FILE.
+=============================================================================
+
+OVERVIEW OF THE LM_SENSORS PACKAGE AND SUMMARY OF SUPPORTED DEVICES
+
+This is the completely rewritten version 2 of lm_sensors, a collection of
+modules for general SMBus access and hardware monitoring.
+Version 1 is now officially unsupported.
+
+WARNING! Starting with lm_sensors version 2.5.0, only kernels 2.2.0 and
+later are supported. Use lm_sensors version 2.4.5 if you use an older
+kernel.
+
+WARNING! If you downloaded this package through our CVS archive, you walk
+the cutting edge. Things may not even compile! On the other hand, you will
+be the first to profit from new drivers and other changes. Have fun!
+
+WARNING! Many things have changed since version 2.4.0. Most importantly,
+you need the i2c package.  It should have come bundled with lm_sensors,
+but you may need to download it separately at:
+  http://www.netroedge.com/~lm78
+Users of kernel 2.3.34 and later (and also all soon to be 2.4.x) kernels
+will be happy to know that the new I2C implementation has been included 
+in the kernel; you may still need to patch it with the latest version 
+if lm_sensors does not compile.
+
+
+At least the following I2C/SMBus adapters are supported:
+  Acer Labs M1533, M1535, and M1543C
+  AMD 756
+  Apple Hydra (used on some PPC machines)
+  Intel I801 ICH/ICH0/ICH2 (used in Intel 810, 810E, 815E, 820, 840 chipsets)
+  Intel PIIX4 (used in many Intel chipsets)
+  Intel I810/I815 GMCH
+  ServerWorks OSB4
+  SiS 5595
+  3Dfx Voodoo 3 and Banshee
+  VIA Technologies VT82C586B, VT82C596A/B, and VT82C686A
+
+
+At least the following hardware sensor chips are supported:
+  Analog Devices ADM1021, ADM1021A, ADM1022,
+                 ADM1023, ADM1025, and ADM9240
+  Asus AS99127F
+  Dallas Semiconductor DS75, DS1621, DS1625, DS1775, and DS1780
+  Hewlett Packard Maxilife (several revisions including '99 NBA)
+  Genesys Logic GL518SM (rev 00, 80), GL520SM, GL523SM
+  Intel Xeon processor embedded sensors
+  Maxim MAX1617 and MAX1617A
+  Myson MTP008
+  National Semicoductor LM75, LM77, LM78, LM78-J, LM79,
+                        LM80, LM81, LM84, and LM87
+  SiS 5595 embedded sensors
+  TelCom TCN75
+  TI THMC10 and THMC50
+  VIA Technologies VT82C686A embedded sensors
+  Winbond W83781D, W83782D, W83783S, and W83627HF
+
+
+We also support some miscellaneous chips:
+  Linear Technologies LTC1710
+  SDRAM Dimms with Serial Presence Detect EEPROMs
+  Intel Xeon processor embedded EEPROMs
+  DDC Monitor embedded EEPROMs
+
+
+We could use some testers of hardware. If you own a specific monitoring
+chip or I2C bus, and are willing to help us out, please contact us - even
+if you did so before. Even if you have no programming knowledge, you could
+help us by running new modules and reporting on the results and output. 
+If you want to offer more substantial help, this is very welcome too, of
+course.
+
+
+Don't ask us whether we support a particular mainboard; we do not know.
+We *do* know what hardware we support, but usually, it is easier to
+install everything and run sensors-detect. It will tell you what hardware
+you have (and incidentally, what corresponding drivers are needed). You
+could also take a look at http://mbm.livewiredev.com/
+(this lists chips found on many mainboard, but regrettable, not the adapters
+on them) or http://web01.fureai.or.jp/~hirobo/project/reserch_project.html
+(yes, it is japanese; you want the ninth column, and it again lists only
+chips, not adapters).
+
+
+SMBus, also known as System Management Bus, is a protocol for communicating
+through a I2C ('I squared C') bus. Many modern mainboards have a System
+Management Bus. There are a lot of devices which can be connected to a
+SMBus; the most notable are modern memory chips with EEPROM memories and
+chips for hardware monitoring.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used
+by mainboard builders for approximately the same results.
+
+Hardware monitoring chips are often connected to the SMBus, but often they
+can also be connected to the ISA bus. The modules in this package usually
+support both ways of accessing them.
+
+Because the SMBus is just a special case of the generalized I2C bus, we
+can simulate the SMBus protocol on plain I2C busses. These busses are
+sometimes used in other parts of your computer. If a supported chip is
+attached to one of these additional busses, they can be used too.
+
+Please read INSTALL before trying to compile and install these modules.
+There is a lot of additional documentation in the doc/ subdirectory.
+Amnong these is a list of supported busses and chips. Regrettably, there
+are too many mainboards to keep a list of busses and chips used on them.
+On the other hand, we provide a program called 'sensors-detect' which
+tries to figure out what hardware is available on your system.
+
+The developers of this package can be reached through the email address
+<sensors@stimpy.netroedge.com>. Do not hesitate to mail us if you have 
+questions, suggestions, problems, want to contribute, or just want to 
+report it works for you. But please try to read the documentation and
+FAQ before you ask any questions!
+
+The latest version of this package can always be found on our homepage:
+http://www.netroedge.com/~lm78. Pre-release versions can be retrieved
+through anonymous CVS; see doc/cvs for more information.
+
+This whole package is copyright (c) 1998 - 2001 by Frodo Looijaard and 
+Philip Edelbrock, except for a few drivers written by
+other people. See the individual files for more exact copyright information.  
+This package may be distributed according to the GNU General Public License 
+(GPL), as included in the file COPYING.
+
+Note that at this moment, libsensors falls under the GPL, not the LGPL.
+In more human language, that means it is FORBIDDEN to link any application
+to the library, even to the shared version, if the application itself
+does not fall under the GPL. This will probably be changed in the future.
+In the meantime, you will have to contact us first if you want to do this.
Index: /lm-sensors/tags/V2-5-5/RPM/lm_sensors-rh.spec
===================================================================
--- /lm-sensors/tags/V2-5-5/RPM/lm_sensors-rh.spec	(revision 809)
+++ /lm-sensors/tags/V2-5-5/RPM/lm_sensors-rh.spec	(revision 809)
@@ -0,0 +1,172 @@
+##This source RPM build the foloowing binary packages:
+## lm_sensors -- user space stuff
+## lm_sensors-devel -- user space stuff for the application development
+## lm_sensors-drivers -- kernel space drivers.
+
+##Dependencies: lm_sensors requires lm_sensors_drivers
+##              lm_sensors-devel requires lm_sensors
+##              lm_sensors-drivers requires new i2c code
+
+## lm_sensors and lm_sensors-devel can be distributed easily as binary
+## packages. They will be compatible with different configurations.
+
+## WARNING!!! lm_sensors-drivers must be compiled for the the same kernel
+## that will run on the target machine. This implies the same kernel
+## version and the  same kernel configuration. Thus, this binary package
+## can be provided by distribution vendors only for their stock distribution
+## kernels. If you use a custom configured kernel, you must rebuild this
+## package. To protect the innocent, we define kversion and make the
+## resulting package dependable on the specific version of the kernel.
+
+#This spec file is good for stock kernels of RedHat based distributions.
+#If you run a stock smp kernel, the package suitable for smp kernel will be
+#built. If you run a stock up kernel, the package suitable for up kernel will be
+#built.
+
+## The only customizable variable is prefix.
+## If i2c has been installed into a location different from prefix or /usr;
+## you must define I2C_HEADERS below.
+## This package IS relocatable (change prefix to relocate).
+
+%define prefix /usr
+
+#Define your kernel version here.
+%define smptag %(uname -r| cut -f2 -d - |sed 's/[0-9]//g')
+%define versiontag %(uname -r|sed 's/smp//')
+%define kversion %(uname -r)
+%define kname kernel%(echo %{smptag}|sed 's/smp/-smp/')
+
+%define I2C_HEADERS %(rpm -ql kernel-i2c|grep include/linux/i2c.h|head -1|sed 's!/linux/i2c.h!!')
+
+%define name lm_sensors
+%define ver 2.5.0
+Summary: Hardware Health Monitoring Tools
+Name: %{name}
+Version: %{ver}
+Release: 1rh
+Group: Applications/System
+Copyright: GPL
+Source0: http://www.lm-sensors.nu/lm-sensors/archive/%{name}-%{ver}.tar.gz
+Buildroot: /var/tmp/%{name}
+Docdir: %{prefix}/doc
+Requires: %{name}-drivers >= %{ver}
+Url: http://www.netroedge.com/~lm78/
+##For officially distributed packages, please sign below
+Packager: Constantine Gavrilov <const-g@xpert.com>
+Distribution: RedHat 6.1
+
+%package drivers
+Summary: Chip and bus drivers for general SMBus access and hardware monitoring.
+Group: System Environment/Kernel
+Copyright: GPL
+Version: %{ver}%{smptag}
+Requires: kernel-i2c >= %{ver}, %{kname} = %{versiontag}
+
+%package devel
+Summary: Development environment for hardware health monitoring applications
+Group: Development/Libraries
+Copyright: GPL 
+Requires: %{name} = %{ver}
+
+%description
+This package contains a collection of user space tools for general SMBus
+access and hardware monitoring. SMBus, also known as System Management Bus,
+is a protocol for communicating through a I2C ('I squared C') bus. Many modern
+mainboards have a System Management Bus. There are a lot of devices which can
+be connected to a SMBus; the most notable are modern memory chips with EEPROM
+memories and chips for hardware monitoring.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used
+by mainboard builders for approximately the same results.
+
+%description drivers
+This package contains a collection of kernel modules for general SMBus
+access and hardware monitoring. SMBus, also known as System Management Bus,
+is a protocol for communicating through a I2C ('I squared C') bus. Many modern
+mainboards have a System Management Bus. There are a lot of devices which can
+be connected to a SMBus; the most notable are modern memory chips with EEPROM
+memories and chips for hardware monitoring.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used
+by mainboard builders for approximately the same results.
+
+Hardware monitoring chips are often connected to the SMBus, but often they
+can also be connected to the ISA bus. The modules in this package usually
+support both ways of accessing them.
+
+%description devel
+This package contains environment for development of user space applications
+for general SMBus access and hardware monitoring. SMBus, also known as
+System Management Bus, is a protocol for communicating through a I2C
+('I squared C') bus. Many modern mainboards have a System Management Bus.
+There are a lot of devices which can be connected to a SMBus; the most
+notable are modern memory chips with EEPROM memories and chips for hardware
+monitoring.
+
+%prep
+%setup
+
+%build
+#even for non-SMP systems parallel make will build faster
+if [ %{smptag} = smp ]; then
+ make -j4 MODVER=1 SMP=1 I2C_HEADERS=%{I2C_HEADERS}
+else
+ make -j4 MODVER=1 SMP=0 I2C_HEADERS=%{I2C_HEADERS}
+fi
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{prefix} \
+	MODDIR=/lib/modules/%{kversion}/misc
+#back up stock config
+cp -a $RPM_BUILD_ROOT/etc/sensors.conf $RPM_BUILD_ROOT/etc/sensors.ex
+
+%post
+ldconfig || /bin/true
+echo "please run \`%{prefix}/sbin/sensors-detect' to configure the sensors."
+
+%postun
+ldconfig || /bin/true
+
+%post drivers
+depmod -a || /bin/true
+
+%postun drivers
+depmod -a || /bin/true
+
+%files
+%config /etc/sensors.conf
+%config /etc/sensors.ex
+%{prefix}/bin/*
+%{prefix}/lib/lib*.so.*
+%dir %{prefix}/man/man1
+%dir %{prefix}/man/man5
+%{prefix}/man/man1/*
+%{prefix}/man/man5/*
+%{prefix}/sbin/*
+%doc BACKGROUND BUGS CHANGES CONTRIBUTORS INSTALL README TODO
+%doc doc/{FAQ,cvs,fan-divisors,modules,progs,temperature-sensors}
+%doc doc/{useful_addresses.html,version-2}
+
+%files drivers
+%dir /lib/modules/%{kversion}
+%dir /lib/modules/%{kversion}/misc
+/lib/modules/%{kversion}/misc/*
+%{prefix}/include/linux/*
+%doc doc/busses doc/chips doc/developers doc/kernel
+
+%files devel
+%dir %{prefix}/include/sensors
+%{prefix}/include/sensors/*.h
+%{prefix}/lib/*.a
+%{prefix}/lib/*.so
+%dir %{prefix}/man/man3
+%{prefix}/man/man3/*
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+rm -rf $RPM_BUILD_DIR/%{name}-%{ver}
Index: /lm-sensors/tags/V2-5-5/RPM/README
===================================================================
--- /lm-sensors/tags/V2-5-5/RPM/README	(revision 809)
+++ /lm-sensors/tags/V2-5-5/RPM/README	(revision 809)
@@ -0,0 +1,65 @@
+##by Constantine Gavrilov <const-g@xpert.com>
+
+Use lm_sensors-rh.spec to build against stock kernels of RedHat based
+distributions.
+Use lm_sesnosrs.spec to build against custom built kernels.
+
+Short instructions on how to build:
+
+1) Put lm_sesnosrs.spec into SPECS directory of your rpm tree
+(/usr/src/redhat/SPECS on RedHat, /usr/src/packages/SPECS on SuSe,
+/usr/src/RPM/SPECS on Mandrake).
+
+2) Put lm_sensors tarball in the SOURCES directory of your RPM tree (see
+   examples above).
+
+3) Adjust the following variables in lm_sesnosrs.spec:
+ a) prefix - where the stuff gets installed;
+ b) kversion - kernel version you are running;
+ c) mversion (if necessary) - defines path for modules' install (equal to
+    kversion by default) -- modules will be installed to /lib/modules/%{mversion}. 
+ d) ver - version of lm_sensors you are compiling (if necessary);
+ e) Group - to which groups the packages belong  (if necessary).
+ f) If i2c has been installed into a location defferent form prefix or /usr,
+    you must ajust I2C_HEADERS as well.
+ 
+4) Type `rpm -bb lm_sensors.spec' from the SPECS directory.
+5) Install the resulting binary packages.
+
+############################################################################
+
+To build this rpm you need (apart from the rpm program):
+1) C development environment;
+2) kernel sources and headers for the same version of kernel that you are
+   running on your machine;
+3) new i2c code installed -- if you are not running the latest 2.3.x kernels, 
+   get and install the new i2c code from http://www.netroedge.com/~lm78/.
+
+############################################################################
+############################################################################
+
+This SPEC file will build the following binary packages:
+
+ lm_sensors -- user space stuff
+ lm_sensors-devel -- user space stuff for the application development
+ lm_sensors-drivers -- kernel space drivers.
+
+Dependencies: lm_sensors requires lm_sensors_drivers
+			  lm_sensors-devel requires lm_sensors
+			  lm_sensors-drivers requires new i2c code
+
+lm_sensors and lm_sensors-devel can be distributed easily as binary
+packages. They will be compatible with different configurations.
+
+WARNING!!! lm_sensors-drivers must be compiled for the the same kernel
+that will run on the target machine. This implies the same kernel
+version and the  same kernel configuration. Thus, this binary packages
+can be provided by distribution vendors only for their stock distribution
+kernels. If you use a custom configured kernel, you must rebuild this
+package. To protect the ignorant, we define kversion and make the
+resulting package dependable on the specific version of the kernel.
+
+#############################################################################
+
+For help on building this rpms, suggestions, comments, etc,  you may contact me
+at <const-g@xpert.com> .
Index: /lm-sensors/tags/V2-5-5/RPM/lm_sensors.spec
===================================================================
--- /lm-sensors/tags/V2-5-5/RPM/lm_sensors.spec	(revision 809)
+++ /lm-sensors/tags/V2-5-5/RPM/lm_sensors.spec	(revision 809)
@@ -0,0 +1,165 @@
+##This source RPM build the foloowing binary packages:
+## lm_sensors -- user space stuff
+## lm_sensors-devel -- user space stuff for the application development
+## lm_sensors-drivers -- kernel space drivers.
+
+##Dependencies: lm_sensors requires lm_sensors_drivers
+##              lm_sensors-devel requires lm_sensors
+##              lm_sensors-drivers requires new i2c code
+
+## lm_sensors and lm_sensors-devel can be distributed easily as binary
+## packages. They will be compatible with different configurations.
+
+## WARNING!!! lm_sensors-drivers must be compiled for the the same kernel
+## that will run on the target machine. This implies the same kernel
+## version and the  same kernel configuration. Thus, this binary package
+## can be provided by distribution vendors only for their stock distribution
+## kernels. If you use a custom configured kernel, you must rebuild this
+## package. To protect the innocent, we define kversion and make the
+## resulting package dependable on the specific version of the kernel.
+
+## The only customizable variables are prefix and kversion.
+## If you have used a custom tug in the vesrion of your kernel rpm,
+## you may need to adjust mversion as well. 
+
+## If i2c has been installed into a location defferent from prefix or /usr;
+## you must define I2C_HEADERS below.
+## This package IS relocatable (change prefix to relocate).
+
+%define prefix /usr/local
+%define I2C_HEADERS %{prefix}/include
+
+#Define your kernel version here.
+%define kversion 2.2.14
+%define mversion %{kversion}
+#Another example for the above: %define kversion 2.2.14-SMP
+
+%define name lm_sensors
+%define ver 2.5.0
+Summary: Hardware Health Monitoring Tools
+Name: %{name}
+Version: %{ver}
+Release: 1
+Group: Applications/System
+Copyright: GPL
+Source0: http://www.lm-sensors.nu/lm-sensors/archive/%{name}-%{ver}.tar.gz
+Buildroot: /var/tmp/%{name}
+Docdir: %{prefix}/doc
+Requires: %{name}-drivers >= %{ver}
+Url: http://www.netroedge.com/~lm78/
+
+##For officially distributed packages, please sign below
+##Packager: Lm_sensors Group <sensors@stimpy.netroedge.com> 
+
+%package drivers
+Summary: Chip and bus drivers for general SMBus access and hardware monitoring.
+Group: Base/Kernel
+###Latest RedHat distributions define a different Group: Group: System Environment/Kernel
+
+Copyright: GPL
+Requires: kernel-i2c >= %{ver}, kernel = %{kversion}
+
+%package devel
+Summary: Development environment for hardware health monitoring applications
+Group: Development/Libraries
+Copyright: GPL 
+Requires: %{name} = %{ver}
+
+%description
+This package contains a collection of user space tools for general SMBus
+access and hardware monitoring. SMBus, also known as System Management Bus,
+is a protocol for communicating through a I2C ('I squared C') bus. Many modern
+mainboards have a System Management Bus. There are a lot of devices which can
+be connected to a SMBus; the most notable are modern memory chips with EEPROM
+memories and chips for hardware monitoring.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used
+by mainboard builders for approximately the same results.
+
+%description drivers
+This package contains a collection of kernel modules for general SMBus
+access and hardware monitoring. SMBus, also known as System Management Bus,
+is a protocol for communicating through a I2C ('I squared C') bus. Many modern
+mainboards have a System Management Bus. There are a lot of devices which can
+be connected to a SMBus; the most notable are modern memory chips with EEPROM
+memories and chips for hardware monitoring.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used
+by mainboard builders for approximately the same results.
+
+Hardware monitoring chips are often connected to the SMBus, but often they
+can also be connected to the ISA bus. The modules in this package usually
+support both ways of accessing them.
+
+%description devel
+This package contains environment for development of user space applications
+for general SMBus access and hardware monitoring. SMBus, also known as
+System Management Bus, is a protocol for communicating through a I2C
+('I squared C') bus. Many modern mainboards have a System Management Bus.
+There are a lot of devices which can be connected to a SMBus; the most
+notable are modern memory chips with EEPROM memories and chips for hardware
+monitoring.
+
+%prep
+%setup
+
+%build
+#even for non-SMP systems parallel make will build faster
+make -j4 I2C_HEADERS=%{I2C_HEADERS}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{prefix}
+#back up stock config
+cp -a $RPM_BUILD_ROOT/etc/sensors.conf $RPM_BUILD_ROOT/etc/sensors.ex
+
+%post
+ldconfig || /bin/true
+echo "please run \`%{prefix}/sbin/sensors-detect' to configure the sensors."
+
+%postun
+ldconfig || /bin/true
+
+%post drivers
+depmod -a || /bin/true
+
+%postun drivers
+depmod -a || /bin/true
+
+%files
+%config /etc/sensors.conf
+%config /etc/sensors.ex
+%{prefix}/bin/*
+%{prefix}/lib/lib*.so.*
+%dir %{prefix}/man/man1
+%dir %{prefix}/man/man5
+%{prefix}/man/man1/*
+%{prefix}/man/man5/*
+%{prefix}/sbin/*
+%doc BACKGROUND BUGS CHANGES CONTRIBUTORS INSTALL README TODO
+%doc doc/{FAQ,cvs,fan-divisors,modules,progs,temperature-sensors}
+%doc doc/{useful_addresses.html,version-2}
+
+%files drivers
+%dir /lib/modules/%{mversion}
+%dir /lib/modules/%{mversion}/misc
+/lib/modules/%{mversion}/misc/*
+%dir %{prefix}/include/linux
+%{prefix}/include/linux/*.h
+%doc doc/busses doc/chips doc/developers doc/kernel
+
+%files devel
+%dir %{prefix}/include/sensors
+%{prefix}/include/sensors/*.h
+%{prefix}/lib/*.a
+%{prefix}/lib/*.so
+%dir %{prefix}/man/man3
+%{prefix}/man/man3/*
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+rm -rf $RPM_BUILD_DIR/%{name}-%{ver}
Index: /lm-sensors/tags/V2-5-5/BUGS
===================================================================
--- /lm-sensors/tags/V2-5-5/BUGS	(revision 738)
+++ /lm-sensors/tags/V2-5-5/BUGS	(revision 738)
@@ -0,0 +1,62 @@
+Except for many unimplemented things (listed in the TODO file), there may be
+true bugs too. If you experience any problems, do not hesitate to mail
+sensors@stimpy.netroedge.com. But read the documentation first, please!
+
+Pre-2.1.58 /proc directory Oops
+===============================
+
+There is a problem in pre 2.1.58 kernels that can make the kernel Oops. You
+can trigger this Oops if you have opened any file, or are in any directory,
+created by a module. If you remove the module at such a moment, successive
+access to those files or directories will make the kernel complain through
+an Oops. There is really no good way to solve this. Stock kernel modules
+exhibit the same problem, by the way. Kernels from 2.1.58 onwards have new
+fill_inode() semantics; using this function, we can increase the module use
+count while a module file or directory is accessed. This solves the problem,
+because it makes it impossible to remove the module.
+Alan Cox (alan@lxorguk.ukuu.org.uk), the maintainer of 2.0 kernels, has
+said he will consider patches that backport this 2.1 feature; perhaps it
+will be in 2.0.37. Until then, be careful when you unload modules.
+
+
+2.2.x cdrom.o Oops
+==================
+
+Module cdrom.o in all 2.2 kernels conflicts with our sensors.o module.
+You can get an Oops if cdrom.o is unloaded after sensors.o was loaded.
+Below is a small diff that you can apply to the kernel to correct this.
+Another solution is to make sure cdrom.o is never unloaded, or to make
+sure it is not a module but compiled into the kernel proper.
+
+The diff corrects a long-standing /proc bug. It will go into kernel 2.2.2
+or later in a somewhat modified form. It was written by Jens Axboe 
+<axboe@image.dk>.
+
+-----cut here-----
+--- virgin/kernel/sysctl.c      Sat Jan  9 07:54:16 1999
++++ linux/kernel/sysctl.c       Mon Feb  1 23:44:58 1999
+@@ -559,12 +559,12 @@
+                        unregister_proc_table(table->child, de);
+                }
+                /* Don't unregister proc directories which still have
+-                  entries... */
+-               if (!((de->mode & S_IFDIR) && de->subdir)) {
++                  entries or are still being used... */
++               if (!((de->mode & S_IFDIR) && de->subdir) && !de->count)
+{
+                        proc_unregister(root, de->low_ino);
+                        table->de = NULL;
+                        kfree(de);
+-               } 
++               }
+        }
+ }
+-----cut here-----
+
+
+Kernel i2c conflict
+===================
+
+An older version of the i2c modules is distributed in 2.2 and late 2.1
+kernels. If you try to use it at the same moment as our i2c modules, you
+may get into trouble. This should be fixed for lm_sensors-2.4.0 and newer.
Index: /lm-sensors/tags/V2-5-5/TODO
===================================================================
--- /lm-sensors/tags/V2-5-5/TODO	(revision 779)
+++ /lm-sensors/tags/V2-5-5/TODO	(revision 779)
@@ -0,0 +1,72 @@
+Many, many things. Most notably:
+
+I2C CODE CHANGES
+================
+
+* Update all documentation
+
+KERNEL MODULES
+==============
+
+* Add string function in sensors.o
+* ALL: cleanup_module is void; check also that cleaning up is done
+  properly, now we know that the module will be unloaded whatever we
+  do.
+* ALL chip drivers: add a readonly insmod option so we don't cause APM/ACPI
+  to go insane.
+* gl518sm: using iterate==2 causes a tread to be started. this thread remains
+  even after setting iterate==1 and/or removing the gl518sm module
+  <koenig@uranus.tat.physik.uni-tuebingen.de>
+* LM78 detection: Tom Webster has proven that the reset bit in the 
+  id register can be one (strange!)
+* maxilife.c: Introduce new insmod variables
+* maxilife.c: Round REG_TO_VID correctly
+* icspll.c: Rewrite. The current implementation simply can't work at all.
+  It needs i2c-level access (too bad for SMBus-only adapters).
+* w83781d: Some features are still unsupported for the W8378[23].
+  Also, alarms seem to give strange results
+  sometimes, and there are some other minor problems, as indicated by
+  Jonathan Bradshaw <jonathan@NrgUp.Com>
+* Support 10-bit addresses. At this moment, they are supported nowhere, except
+  in Simon Vogl's i2c modules.
+* Better general locking, or at least a once-over to check no race-conditions
+  remain. This is part of the SMP-safeness, and can better be done at once.
+* Test SIS5595(/91/98) sensor support and SMBus support
+  Thomas Dickel <Thomas.Dickel@mni.fh-giessen.de> owns one SIS5595.
+* adm9240: check whether the current voltage computations are correct.
+  Probably not, as they are different from the datasheet specifications.
+  Also check for the supported dallas chip.
+* lm80: Check how OS alarms work. At page 17 of the sheet, it tells
+  something completely different from the description at page 25.
+* gl518sm: Assume that new values are close to old values, so start with
+  trying to use a small range near the old values (already partially done)
+* lm77: Add detection in lm75.c and sensors-detect to distinguish from
+  an lm75. Add support for the two new registers.
+* Add MTP006F chip: http://www.myson.com/Pcd/MTP006/Mtp006.pdf
+  gfiala@s.netic.de (Guido Fiala) has one on his mainboard
+* Figure out what chips are appearing at 0x08 and 0x30
+
+LIBRARY
+=======
+
+* reload does not work (Bison/Flex problem?).
+* Some library routines are pretty inefficient right now.
+* Library should be split in more separate files, for better linking
+  behaviour.
+* Some adm9240 labels start with a digit; change this, it leads to confusing
+  syntax in the conf file (they have to be quoted)
+
+PROGRAMS
+========
+
+* Sensors program needs to print a + before lm75 temperatures
+* Sensors program needs to be extended for many new chips
+
+OTHERS
+======
+
+* Examine watchdog-4.3 or later of meskes@debian.org (Michael Meskes)
+  tsx-11.mit.edu /pub/linux/sources/sbin or 
+  sunsite.unc.edu /pub/linux/system/daemons/watchdog
+* Add experimental drivers
+
Index: /lm-sensors/tags/V2-5-5/doc/kernel/file_operations
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/kernel/file_operations	(revision 152)
+++ /lm-sensors/tags/V2-5-5/doc/kernel/file_operations	(revision 152)
@@ -0,0 +1,91 @@
+This file documents the changes in struct file_operations throughout the 2.1
+kernel series.
+
+Kernels 2.0.x:
+
+struct file_operations {
+        int (*lseek) (struct inode *, struct file *, off_t, int);
+        int (*read) (struct inode *, struct file *, char *, int);
+        int (*write) (struct inode *, struct file *, const char *, int);
+        int (*readdir) (struct inode *, struct file *, void *, filldir_t);
+        int (*select) (struct inode *, struct file *, int, select_table *);
+        int (*ioctl) (struct inode *, struct file *, unsigned int, 
+                      unsigned long);
+        int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
+        int (*open) (struct inode *, struct file *);
+        void (*release) (struct inode *, struct file *);
+        int (*fsync) (struct inode *, struct file *);
+        int (*fasync) (struct inode *, struct file *, int);
+        int (*check_media_change) (kdev_t dev);
+        int (*revalidate) (kdev_t dev);
+};
+
+
+Kernel 2.1.0:
+
+-       int (*lseek) (struct inode *, struct file *, off_t, int);
++       long long (*llseek) (struct inode *, struct file *, long long, int);
+-       int (*read) (struct inode *, struct file *, char *, int);
++       long (*read) (struct inode *, struct file *, char *, unsigned long);
+-       int (*write) (struct inode *, struct file *, const char *, int);
++       long (*write) (struct inode *, struct file *, const char *, unsigned lon
+
+
+Kernel 2.1.23:
+
+-       int (*select) (struct inode *, struct file *, int, select_table *);
++       unsigned int (*poll) (struct file *, poll_table *);
+[The lock field is added behind the revalidate field]
++       int (*lock) (struct inode *, struct file *, int, struct file_lock *);
+
+
+Kernel 2.1.31:
+
+-       void (*release) (struct inode *, struct file *);
++       int (*release) (struct inode *, struct file *);
+
+
+Kernel 2.1.55:
+
+-       int (*readdir) (struct inode *, struct file *, void *, filldir_t);
++       int (*readdir) (struct file *, void *, filldir_t);
+
+
+Kernel 2.1.56:
+
+-       long long (*llseek) (struct inode *, struct file *, long long, int);
++       long long (*llseek) (struct file *, long long, int);
+-       int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
++       int (*mmap) (struct file *, struct vm_area_struct *);
+-       int (*fsync) (struct inode *, struct file *);
++       int (*fsync) (struct file *, struct dentry *);
+-       int (*fasync) (struct inode *, struct file *, int);
++       int (*fasync) (struct file *, int);
+-       int (*lock) (struct inode *, struct file *, int, struct file_lock *);
++       int (*lock) (struct file *, int, struct file_lock *);
+
+
+Kernel 2.1.70:
+
+-       long long (*llseek) (struct file *, long long, int);
++       loff_t (*llseek) (struct file *, loff_t, int);
+-       long (*read) (struct inode *, struct file *, char *, unsigned long);
++       ssize_t (*read) (struct file *, char *, size_t, loff_t *);
+-       long (*write) (struct inode *, struct file *, const char *, 
+                       unsigned long);
++       ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
+
+
+Kernel 2.1.72:
+
+-       unsigned int (*poll) (struct file *, poll_table *);
++       unsigned int (*poll) (struct file *, struct poll_table_struct *);
+
+
+Kernel 2.1.118:
+
+[The flush field is added behind the open field -- stupid...]
++       int (*flush) (struct file *);
+
+
+Last kernel checked: 2.2.0-pre4.
Index: /lm-sensors/tags/V2-5-5/doc/kernel/proc_dir_entry
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/kernel/proc_dir_entry	(revision 152)
+++ /lm-sensors/tags/V2-5-5/doc/kernel/proc_dir_entry	(revision 152)
@@ -0,0 +1,145 @@
+This file documents how to handle the callback function that returns the
+information displayed in a (non-/proc/sys) /proc file.
+
+For kernels before 2.1.29, you have to declare a proc_dir_entry, with
+in the get_info field the procedure handler. For kernels 2.1.29 and later,
+you can use the create_proc_entry function and assign a new value (the
+procedure handler address) to the read_proc field:
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)
+  static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
+                          int unused);
+  static struct proc_dir_entry proc_bus_i2c_dir =
+    {
+      /* low_ino */       0,     /* Set by proc_register_dynamic */
+      /* namelen */       3,     /* The length of the name field */
+      /* name */          "i2c",
+      /* mode */          S_IRUGO | S_IFREG,
+      /* nlink */         1,     
+      /* uid */           0,
+      /* gid */           0,
+      /* size */          0,
+      /* ops */           NULL,
+      /* get_info */      &read_bus_i2c
+    };
+  
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)
+  static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
+                             int *eof , void *private);
+#endif
+
+
+  /* This code fragment is part of the initialization code. You should do
+     several safety checks, like whether create_proc_entry does not return
+     NULL. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)
+  proc_register_dynamic(&proc_root, &proc_bus_i2c_dir);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)
+  proc_bus_i2c = create_proc_entry("i2c",0,proc_root);
+  proc_bus_i2c->read_proc = &read_bus_i2c;
+#endif
+
+
+
+Now for the function itself.
+
+   LINUX KERNEL 2.0.x, and 2.1.x upto 2.1.28
+   =========================================
+   This function is known as the get_info function.
+
+   int get_info(char *buf, char **start, off_t offset, int len, int unused);
+
+   (INPUT)
+     buf: A kernel-space buffer where we can put the information.
+     len: The buffer length (we have a bit of slack here: if we write 
+          'a little' beyond this, things will still work out)
+   (OUTPUT)
+     *buf: The complete contents of the /proc file, starting at the
+           beginning.
+     return value: the number of chars written to buf.
+
+   This is all you have to do if all information fits in the buffer (it is 
+   about 3K in length). You can safely ignore offset and start in this case.
+
+   If your /proc entry gets larger, you have to do a bit more work.
+
+   (INPUT)
+     buf: See above
+     len: see above
+     offset: The offset from the beginning of the /proc file where the user
+             wants to start reading
+   (OUTPUT)
+     *buf: A part of the /proc file. The requested information does not
+           have to start at the beginning.
+     start: this is the place within buf where the information starts (ie.
+            you must use offset to compute this!).
+     return value: The number of valid characters written to buf. Begin
+                   counting at start, not at buf!
+
+   You do not have to worry about not the whole information being in buf.
+   Another call will be done (with a higher offset value) if more is
+   needed.
+
+
+   LINUX KERNEL 2.1.x, from 2.1.29 upwards
+   =======================================
+
+   This function is known as the read_proc function.
+
+   int read_proc(char *buf, char **start, off_t offset, int len, int *eof,
+                 void *private);
+   (INPUT)
+     buf: A kernel-space buffer where we can put the information.
+     len: The buffer length (we have a bit of slack here: if we write 
+          'a little' beyond this, things will still work out)
+   (OUTPUT)
+     *buf: The complete contents of the /proc file, starting at the
+           beginning.
+     eof: You can set this to true (anything but 0) to indicate we have
+          reached the end of the file with this read. Not strictly
+          necessary, but it is more efficient (else this function may be
+          called again).
+     return value: The number of valid characters written to buf. Begin
+                   counting at start, not at buf! 
+
+   If your /proc entry gets larger, you have to do a bit more work.
+
+   (INPUT)
+     buf: See above
+     len: See above
+     offset: The offset from the beginning of the /proc file where the user
+             wants to start reading
+   (OUTPUT)
+     *buf: A part of the /proc file. The requested information does not
+           have to start at the beginning.
+     eof: See above
+     start: this is the place within buf where the information starts (ie.
+            you must use offset to compute this!).
+     return value: The number of valid characters written to buf.
+
+   You do not have to worry about not the whole information being in buf.
+   Another call will be done (with a higher offset value) if more is
+   needed. Ignore private.
+
+   A third way to do this:
+
+   (INPUT)
+     buf: See above
+     len: See above
+     offset: The offset from the beginning of the /proc file where the user
+             wants to start reading
+   (OUTPUT)
+     *buf: A part of the /proc file, with the requested information starting
+           at the beginning.
+     eof: See above
+     start: (long)start is added to the offset pointer after reading. This
+            allows you to play tricks with the file offset, independent of
+            the number of read bytes.
+     return value: the number of chars written to buf.
+   
+
+This document was written by Frodo Looijaard <frodol@dds.nl>. I do not
+accept any responsibility for omissions or errors, but you are free to
+drop me a note if something is wrong or if you miss something.
Index: /lm-sensors/tags/V2-5-5/doc/useful_addresses.html
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/useful_addresses.html	(revision 982)
+++ /lm-sensors/tags/V2-5-5/doc/useful_addresses.html	(revision 982)
@@ -0,0 +1,255 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="GENERATOR" content="Mozilla/4.6 [en] (X11; I; Linux 2.4.0-test5 i586) [Netscape]">
+   <title>I&sup2;C, SMBus and hardware monitoring references</title>
+</head>
+<body>
+
+<h1>
+I&sup2;C, SMBus and hardware monitoring references</h1>
+This document contains all kinds of useful references to I&sup2;C and SMBus
+documents and implementations, and to hardware sensors documentation and
+implementations. Please report any other references you have found!
+<ul>
+<li>
+I&sup2;C</li>
+
+<ul>
+<li>
+<a href="http://www.geocities.com/SiliconValley/9504/mic_b000.htm">General
+overview</a></li>
+
+<li>
+<a href="http://kar10.kar.elf.stuba.sk/~balogh/MM/i2c/i2cindex.htm">I&sup2;C
+FAQ</a></li>
+
+<li>
+<a href="http://ftp.philipsmcu.com">Philips, the I&sup2;C developers</a></li>
+
+<br>This site contains many example programs
+<li>
+<a href="http://voxel.at/prj/i2c">Simon Vogl's
+I&sup2;C-Bus for Linux Page</a> and
+<a href="http://www.voxel.at/prj/i2c">U.S. Mirror
+</a>
+</li>
+
+<li>
+<a href="news://goesser.tk.uni-linz.ac.at/linux.i2c">Simon's I&sup2;C newsgroup</a></li>
+</ul>
+
+<li>
+SMBus</li>
+
+<ul>
+<li>
+<a href="http://www.sbs-forum.org/specs/index.html">SMBus specification
+documents</a></li>
+</ul>
+
+<li>
+I&sup2;C and SMBus implementations</li>
+
+<ul>
+
+<li>
+<a href="http://www.acerlabs.com">Acer Labs M1533/M1543C</a></li>
+<br>Acer Labs SMBus adapter, integrated with many other functions.
+
+<li>
+<a href="http://www.intel.com">Intel PIIX4 (82371AB)</a></li>
+<br>Intel SMBus adapter, integrated with many other functions. There are
+<a href="http://developer.intel.com/design/pcisets/datashts/">datasheet
+for most chipsets</a>.
+
+<li>
+<a href="http://www.via.com.tw">VIA South Bridge (VT85C586B)</a></li>
+<br>VIA I&sup2;C adapter, integrated with many other functions. There is
+a separate <a href="http://www.via.com.tw/support/datasheets.html">section
+with datasheets</a>.
+
+</ul>
+
+<li>
+Hardware monitoring chips</li>
+
+<ul>
+<li>
+<a href="http://www.national.com">National Semiconductors</a></li>
+
+<br>LM75/78/79/80 <a href="http://www.national.com/catalog/AnalogTempSensors.html">reference
+sheets</a>.
+<li>
+<a href="http://www.winbond.com.tw">Winbond</a></li>
+
+<br>W83781D, W83782D, W83783S and W83627HF chips. <a href="http://www.winbond.com.tw/produ/perso7.htm">Data
+sheets</a> can be found here for most Winbond chips.
+<li>
+<a href="http://www.analog.com">Analog Devices</a></li>
+
+<br>ADM1021 and ADM9240 <a href="http://products.analog.com/products_html/list_gen_100_2_1.html">reference
+sheets</a>
+<li>
+<a href="http://www.linear.com">Linear Technologies</a></li>
+
+<br>LTC1710 <a href="http://www.linear.com/prodinfo/dslist.html">data sheets</a>
+<li>
+<a href="http://www.genesyslogic.com">Genesys Logic</a></li>
+
+<br>GL518SM and GL520SM <a href="http://www.genesyslogic.com/products/hardward/index.html">data
+sheets</a>
+<li>
+<a href="http://www.hp.com/">Hewlett Packard</a></li>
+
+<br>HP Maxilife (datasheets only under NDA...)
+<li>
+<a href="http://www.sis.com.tw/">Silicon Integrated Systems Corp.</a></li>
+
+<br>SIS5595 <a href="http://www.sis.com.tw/products/products.htm">data
+sheets</a>
+<li>
+<a href="http://www.maxim-ic.com/">Maxim</a></li>
+
+<br>MAX1617 data sheets
+<li>
+<a href="http://www.dalsemi.com">Dallas</a></li>
+
+<br>DS1780 <a href="http://www.dalsemi.com/Prod_info/Thermal/index.html">data
+sheets</a></ul>
+
+<li>
+Other protocol information</li>
+
+<ul>
+<li>
+<a href="http://developer.intel.com/design/servers/ipmi/">IPMI documentation</a></li>
+
+<br>Intelligent Platform Management Interface, a standard for hardware
+monitor chip reading</ul>
+
+<li>
+Hardware Monitoring Applications using the lm_sensors package</li>
+<ul>
+
+<li>
+<a href="http://stimpy.netroedge.com/intranet/tellerstats/">A web-based
+hardware monitor</a></li>
+<br>This is created by Phil Edelbrock, one of our main project contributors
+
+<li>
+<a href="http://www.multiweb.nl/~s.jonker/">GLS</a></li>
+<br>Stijn Jonker's program to make nice graphs of the lm_sensors output.
+Recommended. Requires mysql package.
+
+<li>
+<a href="http://www.brendy.addr.com/linux/gnomesensors/">GnomeSensors</a></li>
+<br>Brendon Humphrey's Gnome display program for lm_sensors.
+Nice. Recommended.
+
+<li>
+<a href="http://www.brendy.addr.com/linux/klm/klm_home.html">klm</a></li>
+<br>Brendon Humphrey's KDE display program for lm_sensors.
+
+<li>
+<a href="http://schudel.penguinpowered.com/MonitorSensors/">MonitorSensors</a></li>
+<br>Daniel Schudel's Gnome display program for lm_sensors.
+Requires GTK--.
+
+<li>
+<a href="http://www.gedanken.demon.co.uk/procmeter3">Procmeter</a></li>
+<br>A comprehensive system monitoring package. Nice desktop app. Recommended.
+
+<li>
+<a href="http://voyager2.htmlplanet.com/sensors_srvr.html">Sensors Server</a></li>
+<br> Nice client/server application with a html-embedded Java applet.
+
+<ku><A HREF="http://denis.corbin.free.fr/software.html">sentinelle</A>, a
+daemon which warns you when your sensor readings reach critical values.
+
+<li>
+<a href="http://users.ox.ac.uk/~kebl0850/wmlm78/">wmlm78/wmsensors homepage</a></li>
+<br>wmlm78 and wmsensors are graphical extensions of lm_sensors, which
+can be docked in Window Manager.
+
+
+
+</ul>
+
+<li>
+Other Hardware monitoring implementations (not using lm_sensors)</li>
+
+<ul>
+<li>
+<a href="http://mbm.livewiredev.com/">Motherboard Monitor
+</a></li>
+<br>A very comprehensive package for Windows. Supports many many devices.
+Also has a great database of motherboards.
+
+<li>
+<a href="http://www.lysator.liu.se/~alla/files/lm78_stuff/">Alexander Larsson's
+lm78 module</a></li>
+
+<br>This is the original module lm_sensors is based upon
+<li>
+<a href="http://www.tu-chemnitz.de/~ronsc/tree/2_Projects/0_c++/lm78/index.html">Ronald
+Schmidt's lm78 module</a></li>
+
+<li>
+<a href="ftp://ftp.vu.union.edu/pub/users/hagopiar/">Beowulf project lm78
+module</a></li>
+
+<br>Written by Erik Hendriks. This <a href="http://beowulf.gsfc.nasa.gov/software/">Nasa
+site</a> may have a newer version. There is also <a href="http://smile.cpe.ku.ac.th">SCMS
+Cluster Management</a> for Beowulf clusters available, which supports the
+LM78 through the above kernel module.
+<li>
+<a href="http://pweb.uunet.de/ganzfix.duew/">txwd, another lm78 module</a></li>
+
+<li>
+<a href="http://wwwcip.rus.uni-stuttgart.de/~phy10296/index.html">Andr&aacute;s
+Major's LM75 module, and graphical monitor for it</a></li>
+
+<LI><A HREF="http://www.egroups.com/files/solarisonintel/Drivers/">Solaris drivers for the LM78</A>
+</ul>
+
+<li>
+Other useful tools</li>
+
+<ul>
+<li>
+<a href="http://users.ev1.net/~redelm/">CPUBURN</a></li>
+
+<br>CPU-heating program to stress-test marginally-cooled systems and test
+lm_sensors.</ul>
+
+<li>
+Other things</li>
+
+<ul>
+<li>
+<a href="http://www.tip.net.au/~millerp/rmch/recu-make-cons-harm.html">Recursive
+Make considered Harmful</a></li>
+
+<br>A paper on which the version 2 Makefile is based
+<li>
+<a href="http://www.3dhardware.net/features/thermistor/">Building a temperature
+sensor using a thermistor</a> An article on the <a href="http://www.3dhardware.net">3D
+Hardware</a> site.</li>
+
+<li>
+<a href="http://www.thermometrics.com/assets/images/ntcnotes.pdf">NTC Thermistor
+Guide</a></li>
+
+<br>A comprehensive description of the physics and mathematics of NTC Thermistors
+<li>
+
+<a href="http://www.jedec.com/DOWNLOAD/jedec/JEP106I.pdf">Standard Manufacturers
+Identification Code</a></li>
+
+<br>The official list of Manufacturer IDs for EEPROMs.</ul>
+</ul>
+
+</body>
+</html>
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-ali15x3
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-ali15x3	(revision 913)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-ali15x3	(revision 913)
@@ -0,0 +1,126 @@
+Kernel driver `i2c-ali5x3.o'
+
+Status: Complete and tested.
+        Block reads/writes untested (not used by any current chip driver).
+
+Supported adapters:
+  * Acer Labs, Inc. ALI 1533 and 1543C
+    Datasheet: Available at the Acer Labs website (password required)
+
+Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
+	<phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+
+Module Parameters
+-----------------
+
+(none)
+
+
+Description
+-----------
+
+
+Stability
+---------
+Complete and tested.
+SMBus periodically hangs on ASUS P5A motherboards and can only be
+cleared by a power cycle. Cause unknown (see Issues below).
+Block reads/writes untested (not used by any existing sensor driver).
+
+
+Supported Devices
+-----------------
+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 M1541 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
+   Some Aladdin V motherboards:
+	Asus P5A
+	Atrend ATC-5220
+	BCM/GVC VP1541
+	Biostar M5ALA
+	Gigabyte GA-5AX (** Generally doesn't work because the BIOS doesn't
+                            enable the 7101 device! **)
+	Iwill XA100 Plus
+	Micronics C200
+	Microstar (MSI) MS-5169
+
+   "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
+At this time the full data sheets on the web site are password protected,
+however if you contact the ALI office in San Jose
+they may give you the password.
+
+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 (rev 03)
+  00:03.0 Bridge: Acer Laboratories Inc. M7101      <= THIS IS THE ONE WE NEED
+  00:07.0 ISA bridge: Acer Laboratories Inc. M1533 (rev c3)
+  00:0f.0 IDE interface: Acer Laboratories Inc. M5229 (rev c1)
+
+** IMPORTANT **
+** If you have a 1533 or 1543C on the board and you get
+** "ali15x3.o: Error: Can't detect ali15x3!"
+** then run lspci.
+** If you see the 1533 and 5229 devices but NOT the 7101 device,
+** then you must enable ACPI, the PMU, SMB, or something similar
+** in the BIOS. If you can't figure out how to enable the
+** 7101 device in the BIOS, you can try one of the "hotplug" modules -
+** see below.
+** The driver won't work if it can't find the 7101 device.
+
+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.
+
+
+Features
+--------
+This driver controls the SMB Host only.
+The SMB Slave controller on the M15X3 is not enabled.
+This driver does not use interrupts.
+
+
+Issues
+------
+This driver requests the I/O space for only the SMB
+registers. It doesn't use the ACPI region.
+
+On the ASUS P5A motherboard, there are several reports that
+the SMBus will hang
+and this can only be resolved by powering off the computer.
+There may be electrical problems on this board.
+On the P5A, the W83781D sensor chip is on both the ISA and
+SMBus. Therefore the SMBus hangs can generally be avoided
+by accessing the W83781D on the ISA bus only.
+
+
+Hot Plug Modules
+----------------
+There are EXPERIMENTAL "hotplug" modules which will enable the
+m7101 device if it is not enabled on your motherboard.
+Use these at your own risk. Read the README file carefully.
+
+m7101 Version 0.1 for 2.2 and early 2.3 kernels:
+	README:            http://www.netroedge.com/~lm78/misc/README-m7101.txt
+	source and README: http://www.netroedge.com/~lm78/misc/m7101-0.1.tar.gz
+
+m7101 Version 0.2 for 2.4 and later 2.3 kernels:
+	In the directory prog/hotplug in this package.
+	Must be built separately.
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-hydra
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-hydra	(revision 434)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-hydra	(revision 434)
@@ -0,0 +1,31 @@
+Kernel driver `i2c-hydra.o'
+
+Status: Complete and somewhat tested
+
+Supported adapters:
+  * Apple `Hydra' Mac I/O
+    Datasheet: Was publicly available from the Apple website, now seems to
+               have disappeared?!?
+
+Author: Geert Uytterhoeven <geert@linux-m68k.org>
+
+
+Module Parameters
+-----------------
+
+(none)
+
+
+Description
+-----------
+
+i2c-hydra is an i2c bus driver for CHRP motherboards with the Apple Hydra Mac
+I/O.
+
+Your /proc/pci or lspci listing must show this :
+
+ Unknown class: Apple Hydra (rev 2)
+
+To install this driver, simply do
+  # modprobe i2c-hydra  
+
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-voodoo3
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-voodoo3	(revision 699)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-voodoo3	(revision 699)
@@ -0,0 +1,80 @@
+Kernel driver `i2c-voodoo3.o'
+
+Status: 'Beta' and still under development and testing
+
+Supported adapters:
+  * 3dfx Voodoo3 based cards
+  * Voodoo Banshee based cards
+
+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>
+
+Main contact: Philip Edelbrock <phil@netroedge.com>
+	
+The code is based upon Ralph's test code (he did the hard stuff ;')
+
+
+Module Parameters
+-----------------
+
+(none)
+
+
+Description
+-----------
+
+The 3dfx Voodoo3 chip contains two I2C interfaces (aka a I2C 'master'
+or 'host'). 
+
+The first interface is used for DDC (Data Display Channel)
+which is a serial channel through the VGA monitor connector
+to a DDC-compliant monitor. This interface is defined by the
+Video Electronics Standards Association (VESA). The standards
+are available for purchase at http://www.vesa.org .
+
+The second interface is a general-purpose I2C bus.
+The intent by 3dfx was to allow manufacturers to add extra
+chips to the video card such as a TV-out chip such as the BT869 or possibly
+even I2C based temperature sensors like the ADM1021 or LM75.
+
+The 'feature' set of this driver is based on the i2c-piix4 (SMBus)
+features.  I.e., SMBus byte reads and writes, as well as detected faults in
+transmission.  Quick reads/writes, block transactions, word transactions,
+are yet unwritten but are probably not too hard to add.
+
+
+Stability
+---------
+Seems to be stable on the test machine, but needs more testing on other
+machines.
+
+Simultaneous accesses of the DDC and I2C busses may cause errors.
+
+
+Supported Devices
+-----------------
+Specifically, this driver was written and tested on the '3dfx Voodoo3 AGP
+3000' which has a tv-out feature (s-video or composite).  According to the
+docs and discussions, this code should work for any Voodoo3 based cards as
+well as Voodoo Banshee based cards.  The DDC interface has been tested
+on a Voodoo Banshee card.
+	
+Features
+--------
+This driver controls the I2C controller of the Voodoo3 chip.  'byte read'
+transactions and 'data_byte write' transactions are supported, but all else
+is not (but is probably easy to add if the need arises).
+
+Issues
+------
+Probably many, but it seems to work OK on my system. :')
+
+
+External Device Connection
+--------------------------
+The digital video input jumpers give availability to the I2C bus. 
+Specifically, pins 13 and 25 (bottom row middle, and bottom right-end) are
+the I2C clock and I2C data lines, respectively. +5V and GND are probably
+also easily available making the addition of extra I2C/SMBus devices easy
+to implement.
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-piix4
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-piix4	(revision 966)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-piix4	(revision 966)
@@ -0,0 +1,63 @@
+Kernel driver `i2c-piix4.o'
+
+Status: Complete and well-tested
+
+Supported adapters:
+  * Intel 82371AB PIIX4 and PIIX4E
+    Datasheet: Publicly available at the Intel website
+  * ServerWorks southbridges
+    Datasheet: only available via NDA from ServerWorks
+
+Author: Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock
+        <phil@netroedge.com>
+
+
+Module Parameters
+-----------------
+
+* force: int
+  Forcibly enable the PIIX4. DANGEROUS!
+* force_addr: int
+  Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
+
+
+Description
+-----------
+
+The PIIX4 (properly known as the 82371AB) is an Intel chip with a lot of
+functionality. Among other things, it implements the PCI bus. One of its
+minor functions is implementing a System Management Bus. This is a true 
+SMBus - you can not access it on I2C levels. The good news is that it
+natively understands SMBus commands and you do not have to worry about
+timing problems. The bad news is that non-SMBus devices connected to it
+can confuse it mightily. Yes, this is known to happen...
+
+Cat /proc/pci, and see whether it contains an entry like this:
+
+  Bus  0, device   1, function  3:
+    Bridge: Intel 82371AB PIIX4 ACPI (rev 1).
+      Medium devsel.  Fast back-to-back capable.  
+
+Bus and device numbers may differ, but the function number must be identical
+(like many PCI devices, the PIIX4 incorporates a number of different
+'functions', which can be considered as separate devices). If you find such
+an entry, you have a PIIX4 SMBus controller.
+
+On some computers (most notably, some Dells), the SMBus is disabled by
+default. If you use the insmod parameter 'force=1', the kernel module
+ will try to enable it. THIS IS VERY DANGEROUS! If the BIOS did not
+set up a correct address for this module, you could get in big trouble
+(read: crashes, data corruption, etc.). Try this only as a last resort
+(try BIOS updates first, for example), and backup first! An even more
+dangerous option is 'force_addr=<IOPORT>'. This will not only enable the
+PIIX4 like 'force' foes, but it will also set a new base I/O port address.
+The SMBus parts of the PIIX4 needs a range of 8 of these addresses to
+function correctly. If these addresses are already reserved by some other
+device, you will get into big trouble! DON'T USE THIS IF YOU ARE NOT VERY
+SURE ABOUT WHAT YOU ARE DOING!
+
+The PIIX4E is just an new version of the PIIX4; it is supported as well. The
+PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use this
+driver on those mainboards.
+
+The ServerWorks Southbridge is identical to the PIIX4 in I2C support.
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-via
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-via	(revision 516)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-via	(revision 516)
@@ -0,0 +1,46 @@
+Kernel driver `i2c-via.o'
+
+Status: Complete and well-tested
+
+Supported adapters:
+  * VIA Technologies, InC. VT82C586B
+    Datasheet: Publicly available at the VIA website
+
+Author: Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+
+Module Parameters
+-----------------
+
+(none)
+
+
+Description
+-----------
+
+i2c-via is an i2c bus driver for motherboards with VIA chipset.
+
+The following VIA pci chipsets are supported:
+ - MVP3, VP3, VP2/97, VPX/97 
+ - others with South bridge VT82C586B
+
+Your /proc/pci or lspci listing must show this :
+
+ Bridge: VIA Technologies, Inc. VT82C586B ACPI (rev 10)
+
+To install this driver, simply do
+  # modprobe i2c-via  
+
+    Problems?
+ 
+ Q: You have VT82C586B on the motherboard, but not in the listing. 
+ 
+ A: Go to your BIOS setup, section PCI devices or similar.
+    Turn USB support on, and try again. 
+
+ Q: No error messages, but still i2c doesn't seem to work.
+
+ A: This can happen. This driver uses the pins VIA recommends in their
+    datasheets, but there are several ways the motherboard manufacturer
+    can actually wire the lines.
+
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-viapro
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-viapro	(revision 521)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-viapro	(revision 521)
@@ -0,0 +1,38 @@
+Kernel driver `i2c-viapro.o'
+
+Status: Complete but untested
+
+Supported adapters:
+  * VIA Technologies, InC. VT82C686    
+    Datasheet: Publicly available at the VIA website
+
+  * VIA Technologies, InC. VT82C596
+    Datasheet: Available per request
+
+Author: Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+Module Parameters
+-----------------
+
+* force: int
+  Forcibly enable the SMBus controller. DANGEROUS!
+* force_addr: int
+  Forcibly enable the SMBus at the given address. EXTREMELY DANGEROUS!
+
+Description
+-----------
+
+i2c-viapro is a true SMBus host driver for motherboards with one of the
+supported VIA southbridges.
+
+Your /proc/pci or lspci -n listing must show either of these :
+
+ device 1106:3050   (vt82c596 function 3)
+ device 1106:3057   (vt82c686 function 4)
+
+If neither of these show up, you should look in the BIOS for settings like
+enable ACPI / SMBus or even USB.
+
+To install this driver, simply do
+  # modprobe i2c-viapro 
+
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-i801
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-i801	(revision 849)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-i801	(revision 849)
@@ -0,0 +1,54 @@
+Kernel driver `i2c-i801.o'
+
+Status: Tested and stable.
+        Block reads/writes untested (but not used by any current chip driver).
+
+Supported adapters:
+  * Intel 82801AA and 82801AB (ICH and ICH0 - part of the
+    '810' and '810E' chipsets)
+  * Intel 82801BA (ICH2 - part of the '815E' chipset)
+    Datasheets: Publicly available at the Intel website
+
+Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
+        <phil@netroedge.com>, and Mark Studebaker <mdsxyz123@yahoo.com>
+
+
+Module Parameters
+-----------------
+
+* force: int
+  Forcibly enable the ICH. DANGEROUS!
+* force_addr: int
+  Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS!
+
+
+Description
+-----------
+
+The ICH (properly known as the 82801AA), ICH0 (82801AB),
+and ICH2 (82801BA) are Intel chips that are a part of
+Intel's '810' chipset for Celeron-based PCs,
+'810E' chipset for Pentium-based PCs, and newer '815E' chipset.
+
+The ICH chips contain about SEVEN separate PCI functions
+in TWO logical PCI devices.
+An output of lspci will show something similar to the following:
+
+  00:1e.0 PCI bridge: Intel Corporation: Unknown device 2418 (rev 01)
+  00:1f.0 ISA bridge: Intel Corporation: Unknown device 2410 (rev 01)
+  00:1f.1 IDE interface: Intel Corporation: Unknown device 2411 (rev 01)
+  00:1f.2 USB Controller: Intel Corporation: Unknown device 2412 (rev 01)
+  00:1f.3 Unknown class [0c05]: Intel Corporation: Unknown device 2413 (rev 01)
+
+The SMBus controller is function 3 in device 1f.
+Class 0c05 is SMBus Serial Controller.
+
+The ICH chips are quite similar to Intel's PIIX4 chip,
+at least in the SMBus controller.
+
+See the file i2c-piix4 for some additional information.
+
+
+**********************
+The lm_sensors project gratefully acknowledges the support of
+Texas Instruments in the development of this driver.
Index: /lm-sensors/tags/V2-5-5/doc/busses/i2c-i810
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/busses/i2c-i810	(revision 862)
+++ /lm-sensors/tags/V2-5-5/doc/busses/i2c-i810	(revision 862)
@@ -0,0 +1,61 @@
+Kernel driver `i2c-i810.o'
+
+Status: Stable
+
+Supported adapters:
+  * Intel 82810, 82810-DC100, and 82815 (GMCH)
+
+Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
+        <phil@netroedge.com>,
+        Kyösti Mälkki <kmalkki@cc.hut.fi>,
+	and Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+Main contact: Mark Studebaker <mdsxyz123@yahoo.com>
+
+
+Module Parameters
+-----------------
+(none)
+
+
+Description
+-----------
+WARNING: If you have an '810' or '815' motherboard, your standard I2C
+temperature sensors are most likely on the 801's I2C bus.
+You want the i2c-i801.o driver for those, not this driver.
+
+Now for the i2c-i810...
+
+The GMCH chip contains two I2C interfaces.
+
+The first interface is used for DDC (Data Display Channel)
+which is a serial channel through the VGA monitor connector
+to a DDC-compliant monitor. This interface is defined by the
+Video Electronics Standards Association (VESA). The standards
+are available for purchase at http://www.vesa.org .
+
+The second interface is a general-purpose I2C bus.
+It may be connected to a TV-out chip such as the BT869 or possibly
+to a digital flat-panel display.
+
+Stability
+---------
+Apparently stable.
+
+Supported Devices
+-----------------
+Specifically, this driver was written and tested on the '3dfx Voodoo3 AGP
+3000' which has a tv-out feature (s-video or composite).  According to the
+docs and discussions, this code should work for any Voodoo3 based cards as
+well as Voodoo Banshee based cards.  The DDC interface has been tested
+on a Voodoo Banshee card.
+	
+Features
+--------
+Both busses use the i2c-algo-bit driver for 'bit banging' and support
+for specific transactions is provided by i2c-algo-bit.
+
+Issues
+------
+If you enable bus testing in i2c-algo-bit (insmod i2c-algo-bit bit_test=1),
+the test may fail and the i2c-i810 driver won't be inserted.
Index: /lm-sensors/tags/V2-5-5/doc/fan-divisors
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/fan-divisors	(revision 685)
+++ /lm-sensors/tags/V2-5-5/doc/fan-divisors	(revision 685)
@@ -0,0 +1,37 @@
+If you aren't getting the expected readings on your fans,
+try the following:
+
+- Do you get a fan reading in the BIOS or in DOS?
+  If not, you may not have a fan with a tachometer output.
+  Look and see. Fans with tachometer outputs have THREE wires.
+
+- Try experimenting with the fan divisor settings:
+
+
+Fan Divisor	Minimum RPM	Maximum RPM
+
+  1		5314		1350000
+  2		2657		 675000
+  4		1328		 337500
+  8		 664		 168750
+ 16		 332		  84375
+ 32		 166		  42187
+ 64		  83		  21093
+128		  41		  10546
+
+
+Pick a divisor so that the nominal RPM is about 50%
+above the minimum. This is a good compromise between
+margin and accuracy.
+
+If you have a 0 RPM reading some or all of the time,
+increase the divisor until you get good readings.
+
+If you have a nominal reading less than 1.25 times the
+minimum, increase the divisor to give you margin
+so that you will not get spurious alarms.
+
+If you have a nominal reading more than 3 times the
+minimum, decrease the divisor to provide better
+accuracy.
+
Index: /lm-sensors/tags/V2-5-5/doc/version-2
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/version-2	(revision 409)
+++ /lm-sensors/tags/V2-5-5/doc/version-2	(revision 409)
@@ -0,0 +1,23 @@
+There have been many, many changes between version 1 and version 2 of this
+package. Some of the more important ones:
+
+* Fully modular setup. This makes it much easier to support new hardware,
+  and consumes less kernel memory.
+* Full support for I2C busses. This includes the VIA chipset I2C bus. If it
+  is supported by Simon Vogl, it is supported by us.
+* Many things which were done in kernel space are now done in user space.
+  The most obvious result is the absence of /proc/sensors. Use instead the
+  supplied 'sensors' program.
+* No more limits on the number of chips.
+* All detected chips get their own directory under /proc/sys/dev/sensors
+* /proc/bus contains a list of all I2C/SMBus adapters found.
+* All applications which want to use sensor data can now access it through
+  the new libsensors.
+* A configuration file which is read by the above mentioned library, which
+  can (through libsensors) be used by any application, and which describes
+  how all sensor readings are to be interpreted.
+* All code has been completely rewritten.
+* Many new drivers
+
+By now (version 2.3.0) so many things have changed that we do not even try
+to keep the above list up-to-date. Just don't use version 1 anymore.
Index: /lm-sensors/tags/V2-5-5/doc/chips/adm9240
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/adm9240	(revision 824)
+++ /lm-sensors/tags/V2-5-5/doc/chips/adm9240	(revision 824)
@@ -0,0 +1,426 @@
+Kernel driver `adm9240.o'
+=========================
+
+Status: Complete and tested
+
+Supported chips:
+  * Analog Devices ADM9240
+    Prefix `adm9240'
+    Addresses scanned: I2C 0x2c - 0x2f (inclusive)
+    Datasheet: Publicly available at the Analog Devices website
+  * Dallas Semiconductor DS1780
+    Prefix `ds1780'
+    Addresses scanned: I2C 0x2c - 0x2f (inclusive)
+    Datasheet: Publicly available at the Dallas Semiconductor website
+  * National Semiconductor LM81
+    Prefix `lm81'
+    Addresses scanned: I2C 0x2c - 0x2f (inclusive)
+    Datasheet: Publicly available at the National Semiconductor website
+
+
+Author: Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock
+        <phil@netroedge.com>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_adm9240: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `adm9240' chip
+* force_ds1780: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `ds1780' chip
+* force_lm81: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `lm81' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+
+This driver implements support for the Analog Devices ADM9240,
+Dallas Semiconductor DS1780, and National Semiconductor LM81 chips.
+These chips appear to be completely
+identical, except for the manufacturer IDs encoded in two registers, and
+the speed with which they update their sensor readings.
+
+The chips implement six voltage sensors, a temperature sensor,
+two FAN rotation speed sensors, VID lines and some miscellaneous stuff.
+
+Temperatures are measured in degrees Celcius. An alarm is triggered whenever
+the temperature is above the hot limit, and will continue to be generated
+until the temperature has dropped beneath the hysteresis limit. Measurements
+are guaranteed between -40 and +125 degrees. The current temperature
+has a resolution of 0.5 degrees, the limits of 1 degree.
+
+FAN rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. FAN
+readings can be divided by a programmable divider (1, 2, 4 or 8) to give
+the readings more range or accuracy. This is important because some FANs
+report only one 'tick' each rotation, while others report two - making
+all readings twice as high. Not all RPM values can accurately be represented,
+so some rounding is done. With a divider of 2, the lowest representable
+value is around 2600 RPM.
+
+Voltage sensors (also known as IN sensors) report their values in volts.
+An alarm is triggered if the voltage has crossed a programmable minimum
+or maximum limit. Note that minimum in this case always means 'closest to
+zero'; this is important for negative voltage measurements.
+IMPORTANT: The current voltage measurement system is very probably broken.
+Please report back to us if you have a ADM9240 or DS1780!
+
+The VID lines encode the core voltage value: the voltage level your processor
+should work with. This is hardcoded by the mainboard and/or processor itself.
+It is a value in volts. When it is unconnected, you will often find the
+value 3.50 V here.
+
+The analog output can be programmed separatedly. It can take values
+between 0 and 255. What it actually does is mainboard-dependent; it
+might influence the FAN-speed. Please report to us whether this is
+the case with your ADM9240.
+
+In addition to the alarms described above, the CHAS alarm triggers
+if your computer case is open. 
+
+If an alarm triggers, it will remain triggered until the hardware register
+is read at least once. This means that the cause for the alarm may
+already have disappeared!  Note that in the current implementation, all
+hardware registers are read whenever any data is read (unless it is less
+than 1.5 seconds since the last update). This means that you can easily
+miss once-only alarms.
+
+Thes ADM9240 only updates its values every 0.5 seconds; the DS1780 one
+every 2 seconds; and the LM81 every 0.4 seconds.
+Reading them more often will do no harm, but will return 
+'old' values. (NB: theoretically, this could be done much faster for the
+ADM9240, but there is no real reason to do so, and this way the SMBus use 
+is kept within reasonable limits)
+
+
+Chip Features
+-------------
+
+Chip `adm9240'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+              2.5V               NONE               NONE     R     2
+             Vccp1               NONE               NONE     R     2
+              3.3V               NONE               NONE     R     2
+                5V               NONE               NONE     R     2
+               12V               NONE               NONE     R     2
+             Vccp2               NONE               NONE     R     2
+          2.5V_min               2.5V               2.5V     RW    2
+         Vccp1_min              Vccp1              Vccp1     RW    2
+          3.3V_min               3.3V               3.3V     RW    2
+            5V_min                 5V                 5V     RW    2
+           12V_min                12V                12V     RW    2
+         Vccp2_min              Vccp2              Vccp2     RW    2
+          2.5V_max               2.5V               2.5V     RW    2
+         Vccp1_max              Vccp1              Vccp1     RW    2
+          3.3V_max               3.3V               3.3V     RW    2
+            5V_max                 5V                 5V     RW    2
+           12V_max                12V                12V     RW    2
+         Vccp2_max              Vccp2              Vccp2     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+          fan1_min               fan1               fan1     RW    0
+          fan2_min               fan2               fan2     RW    0
+              temp               NONE               NONE     R     1
+         temp_hyst               temp               temp     RW    1
+         temp_over               temp               temp     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+            alarms               NONE               NONE     R     0
+        analog_out               NONE               NONE     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+              2.5V                     SENSORS_ADM9240_IN0               2.5V:3
+             Vccp1                     SENSORS_ADM9240_IN1              Vccp1:3
+              3.3V                     SENSORS_ADM9240_IN2               3.3V:3
+                5V                     SENSORS_ADM9240_IN3                 5V:3
+               12V                     SENSORS_ADM9240_IN4                12V:3
+             Vccp2                     SENSORS_ADM9240_IN5              Vccp2:3
+          2.5V_min                 SENSORS_ADM9240_IN0_MIN               2.5V:1
+         Vccp1_min                 SENSORS_ADM9240_IN1_MIN              Vccp1:1
+          3.3V_min                 SENSORS_ADM9240_IN2_MIN               3.3V:1
+            5V_min                 SENSORS_ADM9240_IN3_MIN                 5V:1
+           12V_min                 SENSORS_ADM9240_IN4_MIN                12V:1
+         Vccp2_min                 SENSORS_ADM9240_IN5_MIN              Vccp2:1
+          2.5V_max                 SENSORS_ADM9240_IN0_MAX               2.5V:2
+         Vccp1_max                 SENSORS_ADM9240_IN1_MAX              Vccp1:2
+          3.3V_max                 SENSORS_ADM9240_IN2_MAX               3.3V:2
+            5V_max                 SENSORS_ADM9240_IN3_MAX                 5V:2
+           12V_max                 SENSORS_ADM9240_IN4_MAX                12V:2
+         Vccp2_max                 SENSORS_ADM9240_IN5_MAX              Vccp2:2
+              fan1                    SENSORS_ADM9240_FAN1               fan1:2
+              fan2                    SENSORS_ADM9240_FAN2               fan2:2
+          fan1_min                SENSORS_ADM9240_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_ADM9240_FAN2_MIN               fan2:1
+              temp                    SENSORS_ADM9240_TEMP               temp:3
+         temp_hyst               SENSORS_ADM9240_TEMP_HYST               temp:2
+         temp_over               SENSORS_ADM9240_TEMP_OVER               temp:1
+               vid                     SENSORS_ADM9240_VID                vid:1
+          fan1_div                SENSORS_ADM9240_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_ADM9240_FAN2_DIV            fan_div:2
+            alarms                  SENSORS_ADM9240_ALARMS             alarms:1
+        analog_out              SENSORS_ADM9240_ANALOG_OUT         analog_out:1
+
+
+Chip `ds1780'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+              2.5V               NONE               NONE     R     2
+             Vccp1               NONE               NONE     R     2
+              3.3V               NONE               NONE     R     2
+                5V               NONE               NONE     R     2
+               12V               NONE               NONE     R     2
+             Vccp2               NONE               NONE     R     2
+          2.5V_min               2.5V               2.5V     RW    2
+         Vccp1_min              Vccp1              Vccp1     RW    2
+          3.3V_min               3.3V               3.3V     RW    2
+            5V_min                 5V                 5V     RW    2
+           12V_min                12V                12V     RW    2
+         Vccp2_min              Vccp2              Vccp2     RW    2
+          2.5V_max               2.5V               2.5V     RW    2
+         Vccp1_max              Vccp1              Vccp1     RW    2
+          3.3V_max               3.3V               3.3V     RW    2
+            5V_max                 5V                 5V     RW    2
+           12V_max                12V                12V     RW    2
+         Vccp2_max              Vccp2              Vccp2     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+          fan1_min               fan1               fan1     RW    0
+          fan2_min               fan2               fan2     RW    0
+              temp               NONE               NONE     R     1
+         temp_hyst               temp               temp     RW    1
+         temp_over               temp               temp     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+            alarms               NONE               NONE     R     0
+        analog_out               NONE               NONE     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+              2.5V                      SENSORS_DS1780_IN0               2.5V:3
+             Vccp1                      SENSORS_DS1780_IN1              Vccp1:3
+              3.3V                      SENSORS_DS1780_IN2               3.3V:3
+                5V                      SENSORS_DS1780_IN3                 5V:3
+               12V                      SENSORS_DS1780_IN4                12V:3
+             Vccp2                      SENSORS_DS1780_IN5              Vccp2:3
+          2.5V_min                  SENSORS_DS1780_IN0_MIN               2.5V:1
+         Vccp1_min                  SENSORS_DS1780_IN1_MIN              Vccp1:1
+          3.3V_min                  SENSORS_DS1780_IN2_MIN               3.3V:1
+            5V_min                  SENSORS_DS1780_IN3_MIN                 5V:1
+           12V_min                  SENSORS_DS1780_IN4_MIN                12V:1
+         Vccp2_min                  SENSORS_DS1780_IN5_MIN              Vccp2:1
+          2.5V_max                  SENSORS_DS1780_IN0_MAX               2.5V:2
+         Vccp1_max                  SENSORS_DS1780_IN1_MAX              Vccp1:2
+          3.3V_max                  SENSORS_DS1780_IN2_MAX               3.3V:2
+            5V_max                  SENSORS_DS1780_IN3_MAX                 5V:2
+           12V_max                  SENSORS_DS1780_IN4_MAX                12V:2
+         Vccp2_max                  SENSORS_DS1780_IN5_MAX              Vccp2:2
+              fan1                     SENSORS_DS1780_FAN1               fan1:2
+              fan2                     SENSORS_DS1780_FAN2               fan2:2
+          fan1_min                 SENSORS_DS1780_FAN1_MIN               fan1:1
+          fan2_min                 SENSORS_DS1780_FAN2_MIN               fan2:1
+              temp                     SENSORS_DS1780_TEMP               temp:3
+         temp_hyst                SENSORS_DS1780_TEMP_HYST               temp:2
+         temp_over                SENSORS_DS1780_TEMP_OVER               temp:1
+               vid                      SENSORS_DS1780_VID                vid:1
+          fan1_div                 SENSORS_DS1780_FAN1_DIV            fan_div:1
+          fan2_div                 SENSORS_DS1780_FAN2_DIV            fan_div:2
+            alarms                   SENSORS_DS1780_ALARMS             alarms:1
+        analog_out               SENSORS_DS1780_ANALOG_OUT         analog_out:1
+
+
+Chip `lm81'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+              2.5V               NONE               NONE     R     2
+             Vccp1               NONE               NONE     R     2
+              3.3V               NONE               NONE     R     2
+                5V               NONE               NONE     R     2
+               12V               NONE               NONE     R     2
+             Vccp2               NONE               NONE     R     2
+          2.5V_min               2.5V               2.5V     RW    2
+         Vccp1_min              Vccp1              Vccp1     RW    2
+          3.3V_min               3.3V               3.3V     RW    2
+            5V_min                 5V                 5V     RW    2
+           12V_min                12V                12V     RW    2
+         Vccp2_min              Vccp2              Vccp2     RW    2
+          2.5V_max               2.5V               2.5V     RW    2
+         Vccp1_max              Vccp1              Vccp1     RW    2
+          3.3V_max               3.3V               3.3V     RW    2
+            5V_max                 5V                 5V     RW    2
+           12V_max                12V                12V     RW    2
+         Vccp2_max              Vccp2              Vccp2     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+          fan1_min               fan1               fan1     RW    0
+          fan2_min               fan2               fan2     RW    0
+              temp               NONE               NONE     R     1
+         temp_hyst               temp               temp     RW    1
+         temp_over               temp               temp     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+            alarms               NONE               NONE     R     0
+        analog_out               NONE               NONE     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+              2.5V                     SENSORS_ADM9240_IN0               2.5V:3
+             Vccp1                     SENSORS_ADM9240_IN1              Vccp1:3
+              3.3V                     SENSORS_ADM9240_IN2               3.3V:3
+                5V                     SENSORS_ADM9240_IN3                 5V:3
+               12V                     SENSORS_ADM9240_IN4                12V:3
+             Vccp2                     SENSORS_ADM9240_IN5              Vccp2:3
+          2.5V_min                 SENSORS_ADM9240_IN0_MIN               2.5V:1
+         Vccp1_min                 SENSORS_ADM9240_IN1_MIN              Vccp1:1
+          3.3V_min                 SENSORS_ADM9240_IN2_MIN               3.3V:1
+            5V_min                 SENSORS_ADM9240_IN3_MIN                 5V:1
+           12V_min                 SENSORS_ADM9240_IN4_MIN                12V:1
+         Vccp2_min                 SENSORS_ADM9240_IN5_MIN              Vccp2:1
+          2.5V_max                 SENSORS_ADM9240_IN0_MAX               2.5V:2
+         Vccp1_max                 SENSORS_ADM9240_IN1_MAX              Vccp1:2
+          3.3V_max                 SENSORS_ADM9240_IN2_MAX               3.3V:2
+            5V_max                 SENSORS_ADM9240_IN3_MAX                 5V:2
+           12V_max                 SENSORS_ADM9240_IN4_MAX                12V:2
+         Vccp2_max                 SENSORS_ADM9240_IN5_MAX              Vccp2:2
+              fan1                    SENSORS_ADM9240_FAN1               fan1:2
+              fan2                    SENSORS_ADM9240_FAN2               fan2:2
+          fan1_min                SENSORS_ADM9240_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_ADM9240_FAN2_MIN               fan2:1
+              temp                    SENSORS_ADM9240_TEMP               temp:3
+         temp_hyst               SENSORS_ADM9240_TEMP_HYST               temp:2
+         temp_over               SENSORS_ADM9240_TEMP_OVER               temp:1
+               vid                     SENSORS_ADM9240_VID                vid:1
+          fan1_div                SENSORS_ADM9240_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_ADM9240_FAN2_DIV            fan_div:2
+            alarms                  SENSORS_ADM9240_ALARMS             alarms:1
+        analog_out              SENSORS_ADM9240_ANALOG_OUT         analog_out:1
+
+
+
+
+Old Documentation
+-----------------
+
+Supports the Analog Devices ADM9240 and the Dallas Semiconductor DS1780.
+These chips are apparently identical except for the manufacturer ID
+register.
+The proc directory will be adm9240-i2c-x-xx for the adm9240 and
+ds1780-i2c-x-xx for the ds1780.
+
+-----------------------------------------------------------------------
+
+This file documents the adm9240 directories for the Analog Devices
+ADM9240 hardware health monitoring chip found on some Intel mainboards
+(and others?).
+
+There will be one directory created for each detected ADM9240 chip.
+Each chip must be on the SMBus. Directories are called things
+like adm9240-i2c-1-2c. /proc/sys/dev/sensors/chips contains the SYSCTL
+values for all chip directories.
+
+Within each ADM9240 directory, you can find the following files:
+* alarms (ADM9240_SYSCTL_ALARMS)
+  A number which indicates which alarms are on. An alarm gets triggered when
+  some limit has been crossed. This number is composed from a 16 bit
+  value which is the combination of the Interrupt Status Register 1
+  (low byte) and 2 (high byte).
+  This file can not be written to.
+  The number is the logical OR of the following components (bit #
+  followed by a description):
+  
+0 +2.5 V_Error Read Only A "1" indicates a high or low limit has been exceeded.
+1 VCCP_Error Read Only A "1" indicates a high or low limit has been exceeded.
+2 +3.3 V_Error Read Only A "1" indicates a high or low limit has been exceeded.
+3 +5 V_Error Read Only A "1" indicates a high or low limit has been exceeded.
+4 Temp_Error Read Only A "1" indicates that a temperature interrupt has been set.
+5 Reserved Read Only Undefined.
+6 FAN1_Error Read Only A "1" indicates that a fan count limit has been exceeded.
+7 FAN2_Error Read Only A "1" indicates that a fan count limit has been exceeded.
+8 +12 V_Error Read Only A "1" indicates a high or low limit has been exceeded.
+9 VCCP2_Error Read Only A "1" indicates a high or low limit has been exceeded.
+10 Reserved Read Only Undefined.
+11 Reserved Read Only Undefined.
+12 Chassis_Error Read Only A "1" indicates chassis intrusion has gone high.
+13 Reserved Read Only Undefined.
+14 Reserved Read Only Undefined.
+15 Reserved Read Only Undefined.
+
+  If accessed through sysctl, this value is a long.
+* fan[1-3] (ADM9240_SYSCTL_FAN[1-2])
+  A list of two numbers. The first is the minimum fan rotation limit; the
+  second is the current fan rotation speed. Both are in RPM (rotations per
+  minute). An alarm is triggered if the rotation speed drops below the
+  limit. The first value can be changed. Not all RPM values can accurately
+  be represented, so some rounding is done.
+  If accessed through sysctl, this is a list of longs.
+* fan_div (LM78_SYSCTL_FAN_DIV)
+  A list of two numbers, one for each fan. Each number can be either 1, 2,
+  4 or 8. It is the internal scaling factor used for the FAN rotations. If
+  you change this number, the FAN readings get more or less detailed, but
+  the range in which they can be read too. Higher numbers give better 
+  resolution, but less range. The first two numbers can be changed, the
+  third not.
+  If accessed through sysctl, this is a list of longs.
+* 2.5V, Vccp1, 3.3V, 5V, 12V, and Vccp2 (ADM9240_SYSCTL_IN[0-5] in the
+  order shown)
+  A list of three numbers. The first is the minimum limit, the second is the
+  maximum limit, and the third is the current value.  Note that
+  minimum means 'closest to zero'; so if the normal value equals -10, a
+  minimum could equal -9, and a maximum -11. Vccp1 and Vccp2 reflect core
+  voltages; they are by default set from the vid values, or set to 2.80V
+  if the vid lines seem to be unconnected. Not all voltage values
+  can be accurately represented, so some rounding is done.
+  The first two numbers can be changed, the third not.
+  If accessed through sysctl, this is a list of longs, each being the voltage
+  times 100.
+* temp
+  A list of three numbers. The first number is the Overtemperature Shutdown
+  value; the second is the Hysteresis value and the third number is the
+  current value. The first two values can be modified. All values are in
+  degrees Celsius. An alarm is issued when the temperature first gets above
+  the Overtemperature Shutdown value; no more alarms are issued until the
+  temperature gets below the Hysteresis value, at which moment another alarm
+  is issued, and the state is reset. A more useful behaviour can be found
+  by setting the Hysteresis value to +127 degrees Celsius; in this case, 
+  alarms are issued during all the time when the actual temperature is 
+  above the Overtemperature Shutdown value.
+  The first two numbers can be changed, the third not.
+  If accessed through sysctl, this is a list of longs, each being the
+  temperature in degrees Celsius times 10.
+* vid
+  The core voltage value (the voltage level your processor should work with),
+  in volts. This is the value IN0 and IN1 are initialized to. If unconnected,
+  it usually reads 3.50V.
+  This number can not be changed.
+  If accessed through sysctl, this is a long, being the voltage times 100.
+* analog_out
+   This is feature is specific to the ADM9240.  It is a DAC voltage
+   control register for the Analog-Out pin.  Valid values are from 255
+   to 0 (with 255 being the highest voltage, and 0 the lowest).  From
+   the docs: "The ADM9240 has a single analog output from an unsigned
+   8-bit DAC which produces 0 V - 1.25 V."
+
+The data for each ADM9240 is updated each 1.5 seconds,
+but only if it is actually read.
+
+Note: Much of this documentation is copied from the LM78 documenation
+(the device from which most featuers of the ADM9240 is modelled from).
+The maintainer of the ADM9240 is Philip Edelbrock
+<phil@netroedge.com>; please forward corrections, suggestions, etc.
+to him.  For further technical information on this chip, I suggest
+getting the Acrobat PDF documentation from www.analog.com.
+
+Dallas documentation is at http://www.dalsemi.com.
Index: /lm-sensors/tags/V2-5-5/doc/chips/ds1621
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/ds1621	(revision 973)
+++ /lm-sensors/tags/V2-5-5/doc/chips/ds1621	(revision 973)
@@ -0,0 +1,80 @@
+Kernel driver `ds1621.o'
+=====================
+
+Status: Complete and tested, alarms experimental
+
+Supported chips: 
+  * Dallas Semiconductor DS1621
+    Prefix: `ds1621'
+    Addresses scanned: I2C 0x48 - 0x4f (inclusive)
+    Datasheet: Publicly available at the Dallas Semiconductor website
+               http://www.dalsemi.com
+  * Dallas Semiconductor DS1625
+    Prefix: `ds1621'
+    Addresses scanned: I2C 0x48 - 0x4f (inclusive)
+    Datasheet: Publicly available at the Dallas Semiconductor website
+               http://www.dalsemi.com
+
+Author: Christian W. Zuckschwerdt <zany@triq.net>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_ds1621: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `ds1621' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+
+The DS1621 implements one temperature sensor. Limits can be set through the
+Overtemperature register and Hysteresis register. Each value can be 
+set and read to half-degree accuracy.
+An alarm is issued when the temperature gets higher then the Overtemperature
+value; or drops below the Hysteresis value.
+
+It stays on until reset by the kernel module.
+
+All temperatures are in degrees Celcius, and are guaranteed within a 
+range of -55 to +125 degrees.
+
+The DS1621 supplies the temperature data in a format common with chips
+like the LM75, LM77 and DS75, DS1775.
+While the DS1621 has three additional bits of accuracy
+(12 vs. 9 for the LM75), the additional bits are not supported.
+
+The driver also supports the DS1625. It does not distinguish the 1625 from
+the 1621. The chip prefix in /proc is always ds1621.
+The difference in the chips is that the 1621 supports additional registers
+for greater accuracy. The driver does not use these registers.
+The DS1625 has been discontinued by Dallas Semi, they recommend the
+DS1621 for new designs.
+
+
+Chip Features
+-------------
+
+Chip `ds1621'
+          LABEL     LABEL CLASS   COMPUTE CLASS ACCESS MAGNITUDE
+           temp            NONE            NONE      R     1
+      temp_hyst            temp            temp      RW    1
+      temp_over            temp            temp      RW    1
+         alarms            NONE            NONE      R     0
+
+          LABEL                           FEATURE SYMBOL     SYSCTL FILE:OFFSET
+           temp                      SENSORS_DS1621_TEMP            temp:3
+      temp_hyst                 SENSORS_DS1621_TEMP_HYST            temp:2
+      temp_over                 SENSORS_DS1621_TEMP_OVER            temp:1
+         alarms                    SENSORS_DS1621_ALARMS          alarms:1
Index: /lm-sensors/tags/V2-5-5/doc/chips/lm80
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/lm80	(revision 746)
+++ /lm-sensors/tags/V2-5-5/doc/chips/lm80	(revision 746)
@@ -0,0 +1,151 @@
+Kernel driver `lm80.o'
+======================
+
+Status: Complete and tested
+
+Supported chips:
+  * National Semiconductors LM80
+    Prefix `lm80'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive)
+
+Author: Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock
+        <phil@netroedge.com>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_lm80: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `lm80' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+
+This driver implements support for the National Semiconductors LM80.
+It is descibed as a `Serial Interface ACPI-Compatible Microprocessor
+System Hardware Monitor'.
+
+The LM80 implements one temperature sensor, two FAN rotation speed sensors,
+seven voltage sensors, alarms, and some miscellaneous stuff.
+
+Temperatures are measured in degrees Celcius. There are two sets of limits
+which operate independently. When the HOT Temperature Limit is crossed,
+this will cause an alarm that will be reasserted until the temperature
+drops below the HOT Hysteresis. The Overtemperature Shotdown (OS) limits
+should work in the same way (but this must be checked; the datasheet
+is unclear about this). Measurements are guaranteed between -55 and
++125 degrees. The current temperature measurement has a resolution of
+0.0625 degrees; the limits have a resolution of 1 degree.
+
+FAN rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. FAN 
+readings can be divided by a programmable divider (1, 2, 4 or 8) to give
+the readings more range or accuracy. This is important because some FANs
+report only one 'tick' each rotation, while others report two - making
+all readings twice as high. Not all RPM values can accurately be represented,
+so some rounding is done. With a divider of 2, the lowest representable
+value is around 2600 RPM.
+
+Voltage ensors (also known as IN sensors) report their values in volts.
+An alarm is triggered if the voltage has crossed a programmable minimum 
+or maximum limit. Note that minimum in this case always means 'closest to
+zero'; this is important for negative voltage measurements. All voltage
+inputs can measure voltages between 0 and 2.55 volts, with a resolution
+of 0.01 volt.
+
+If an alarm triggers, it will remain triggered until the hardware register
+is read at least once. This means that the cause for the alarm may 
+already have disappeared!  Note that in the current implementation, all
+hardware registers are read whenever any data is read (unless it is less
+than 2.0 seconds since the last update). This means that you can easily
+miss once-only alarms.
+
+The LM80 only updates its values each 1.5 seconds; reading it more often
+will do no harm, but will return 'old' values.
+
+
+Chip Features
+-------------
+
+Chip `lm80'
+          LABEL     LABEL CLASS   COMPUTE CLASS ACCESS MAGNITUDE
+            in0            NONE            NONE      R     2
+            in1            NONE            NONE      R     2
+            in2            NONE            NONE      R     2
+            in3            NONE            NONE      R     2
+            in4            NONE            NONE      R     2
+            in5            NONE            NONE      R     2
+            in6            NONE            NONE      R     2
+        in0_min             in0             in0      RW    2
+        in1_min             in1             in1      RW    2
+        in2_min             in2             in2      RW    2
+        in3_min             in3             in3      RW    2
+        in4_min             in4             in4      RW    2
+        in5_min             in5             in5      RW    2
+        in6_min             in6             in6      RW    2
+        in0_max             in0             in0      RW    2
+        in1_max             in1             in1      RW    2
+        in2_max             in2             in2      RW    2
+        in3_max             in3             in3      RW    2
+        in4_max             in4             in4      RW    2
+        in5_max             in5             in5      RW    2
+        in6_max             in6             in6      RW    2
+           fan1            NONE            NONE      R     0
+           fan2            NONE            NONE      R     0
+       fan1_min            fan1            NONE      RW    0
+       fan2_min            fan2            NONE      RW    0
+           temp            NONE            NONE      R     2
+  temp_hot_hyst            temp            temp      RW    2
+   temp_hot_max            temp            temp      RW    2
+   temp_os_hyst            temp            temp      RW    2
+    temp_os_max            temp            temp      RW    2
+       fan1_div            fan1            NONE      RW    0
+       fan2_div            fan2            NONE      RW    0
+         alarms            NONE            NONE      R     0
+
+          LABEL                           FEATURE SYMBOL     SYSCTL FILE:OFFSET
+            in0                         SENSORS_LM80_IN0             in0:3
+            in1                         SENSORS_LM80_IN1             in1:3
+            in2                         SENSORS_LM80_IN2             in2:3
+            in3                         SENSORS_LM80_IN3             in3:3
+            in4                         SENSORS_LM80_IN4             in4:3
+            in5                         SENSORS_LM80_IN5             in5:3
+            in6                         SENSORS_LM80_IN6             in6:3
+        in0_min                     SENSORS_LM80_IN0_MIN             in0:1
+        in1_min                     SENSORS_LM80_IN1_MIN             in1:1
+        in2_min                     SENSORS_LM80_IN2_MIN             in2:1
+        in3_min                     SENSORS_LM80_IN3_MIN             in3:1
+        in4_min                     SENSORS_LM80_IN4_MIN             in4:1
+        in5_min                     SENSORS_LM80_IN5_MIN             in5:1
+        in6_min                     SENSORS_LM80_IN6_MIN             in6:1
+        in0_max                     SENSORS_LM80_IN0_MAX             in0:2
+        in1_max                     SENSORS_LM80_IN1_MAX             in1:2
+        in2_max                     SENSORS_LM80_IN2_MAX             in2:2
+        in3_max                     SENSORS_LM80_IN3_MAX             in3:2
+        in4_max                     SENSORS_LM80_IN4_MAX             in4:2
+        in5_max                     SENSORS_LM80_IN5_MAX             in5:2
+        in6_max                     SENSORS_LM80_IN6_MAX             in6:2
+           fan1                        SENSORS_LM80_FAN1            fan1:2
+           fan2                        SENSORS_LM80_FAN2            fan2:2
+       fan1_min                    SENSORS_LM80_FAN1_MIN            fan1:1
+       fan2_min                    SENSORS_LM80_FAN2_MIN            fan2:1
+           temp                        SENSORS_LM80_TEMP            temp:5
+  temp_hot_hyst               SENSORS_LM80_TEMP_HOT_HYST            temp:2
+   temp_hot_max                SENSORS_LM80_TEMP_HOT_MAX            temp:1
+   temp_os_hyst                SENSORS_LM80_TEMP_OS_HYST            temp:4
+    temp_os_max                 SENSORS_LM80_TEMP_OS_MAX            temp:3
+       fan1_div                    SENSORS_LM80_FAN1_DIV         fan_div:1
+       fan2_div                    SENSORS_LM80_FAN2_DIV         fan_div:2
+         alarms                      SENSORS_LM80_ALARMS          alarms:1
Index: /lm-sensors/tags/V2-5-5/doc/chips/MODPARMS
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/MODPARMS	(revision 412)
+++ /lm-sensors/tags/V2-5-5/doc/chips/MODPARMS	(revision 412)
@@ -0,0 +1,63 @@
+All chip drivers have a few insmod module parameters in common. These
+parameters can be used when a module is inserted, to give some additional
+information about how it should function. In this case, they tell where
+the module should look for what chips. Usually, you don't need them, and
+if you do, you are often told by sensors-detect which ones you need.
+
+I2C/SMBus adapters are numbered in the order they are inserted. If you want
+to know what number an adapter has, please make sure module `i2c-proc' is 
+inserted, and look at file /proc/bus/i2c. The ISA bus always has the
+symbolic number 9191 (`I' is the ninth letter of the alphabet, `S' the
+nineteenth and `A' the first). If you want 'any I2C bus', use '-1' 
+(or 65535).
+
+Each adapter has a number of addresses on I2C/SMBus adapters and/or the
+ISA bus that are always scanned if no insmod parameters are given. The
+parameters below override those addresses that are scanned by default.
+
+All insmod parameters described below take lists of positive integers
+(unsigned, in the range of 0 to 65535) as their arguments.  Integers
+are separated by comma's, and may be given as ordinary decimal numbers,
+as octal numbers (by starting them with a `0') or as hexadecimal numbers
+(by starting them with a `0x'). More information can be found by
+entering `man insmod'.
+
+`force', `force_*', `ignore' and `probe' parameters take pairs of numbers.
+Each first (odd) number is a bus number, each second (even) number is
+an (I2C or ISA) address.
+
+`ignore_range' and `probe_range' parameters take triples of numbers.
+Each first number is a bus number, each second number is the start address
+of a range, and each third number is the end address of a range. Ranges
+are always inclusive.
+
+`probe' and `probe_range' parameters tell the module to scan additional 
+addresses for a specific bus. These are treated just as the ordinary
+addresses scanned. This is useful for some chips that can be anywhere;
+to keep loading time (and false detections) down, only the most common
+addresses are scanned by default.
+
+`ignore' and `ignore_range' parameters tell the module not to scan
+specific addresses. They overrule the default addresses and any 
+addresses specified by `probe' and `probe_range' statements.
+
+The `force' parameter tells the module a supported chip is found at
+a specific address. It overrules all previously mentioned parameters.
+Sometimes, a chip can be in a specific internal state that makes
+detection impossible. If you specify it with the `force' parameter,
+it is first put into a recognizable state. Also, some detection routines
+are skipped. If the module supports several chips, it will still try
+to determine what chip is found on that address. If it can't determine
+this, the address will still not be used.
+
+There is a `force_*' parameter for each type of chip supported by a
+module. This is the strongest statement possible - it says that a
+specific type of chip is found on a specific address, and the module
+will skip all detection and recognition routines. This can lead to
+very strange results...
+
+An example:
+  insmod lm78 force_lm79=9191,0x390 ignore=0,0x2d,1,0x20 probe_range=-1,0,0x7f
+  # ISA address 0x390 contains a LM79
+  # Scan all I2C addresses (from 0 to 0x7f) for all I2C adapters
+  # But skip address 0x2d on adapter 0, and address 0x20 on adapter 1
Index: /lm-sensors/tags/V2-5-5/doc/chips/via686a
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/via686a	(revision 755)
+++ /lm-sensors/tags/V2-5-5/doc/chips/via686a	(revision 755)
@@ -0,0 +1,139 @@
+Kernel driver `via686a.o'
+=========================
+
+Status: Alpha.
+
+Supported chips:
+  * Via 686a Southbridge Integrated Hardware Monitor
+    Prefix: via686a
+    Addresses scanned: ISA in PCI-space encoded address
+    Datasheet: Publicly available at the Silicon Integrated Systems Corp. site.
+
+Authors: Kyösti Mälkki <kmalkki@cc.hut.fi>,
+         Mark D. Studebaker <mdsxyz123@yahoo.com>
+	 Bob Dougherty <bobd@stanford.edu>
+    	 (Some conversion-factor data were contributed by 
+	 Jonathan Teh Soon Yew <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
+
+Module Parameters
+-----------------
+
+none
+
+
+Description
+-----------
+
+The Via 686a southbridge has integrated hardware monitor functionality.
+It also has an I2C bus, but this driver only supports the hardware monitor.
+For the I2C bus driver, see ../busses/i2c-viapro.
+
+The Via 686a implements three temperature sensors, two FAN rotation speed
+sensors, five voltage sensors and alarms.
+
+Temperatures are measured in degrees Celcius. An alarm is triggered once
+when the Overtemperature Shutdown limit is crossed; it is triggered again
+as soon as it drops below the hysteresis value.  A more useful behaviour 
+can be found by setting the hysteresis value to +127 degrees Celcius; in 
+this case, alarms are issued during all the time when the actual temperature
+is above the Overtemperature Shutdown value. Measurements are guaranteed
+between -55 and +125 degrees, with a resolution of 1 degree.
+
+FAN rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. FAN 
+readings can be divided by a programmable divider (1, 2, 4 or 8) to give
+the readings more range or accuracy. This is important because some FANs
+report only one 'tick' each rotation, while others report two - making
+all readings twice as high. Not all RPM values can accurately be represented,
+so some rounding is done. With a divider of 2, the lowest representable
+value is around 2600 RPM.
+
+Voltage sensors (also known as IN sensors) report their values in volts.
+An alarm is triggered if the voltage has crossed a programmable minimum 
+or maximum limit. Note that minimum in this case always means 'closest to
+zero'; this is important for negative voltage measurements. All voltage
+inputs can measure voltages between 0 and 4.08 volts, with a resolution
+of 0.016 volt.
+
+If an alarm triggers, it will remain triggered until the hardware register
+is read at least once. This means that the cause for the alarm may 
+already have disappeared!  Note that in the current implementation, all
+hardware registers are read whenever any data is read (unless it is less
+than 1.5 seconds since the last update). This means that you can easily
+miss once-only alarms.
+
+The driver only updates its values each 1.5 seconds; reading it more often
+will do no harm, but will return 'old' values.
+
+
+Chip Features
+-------------
+
+Chip `via686a'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+               in0               NONE               NONE     R     2
+               in1               NONE               NONE     R     2
+               in2               NONE               NONE     R     2
+               in3               NONE               NONE     R     2
+               in4               NONE               NONE     R     2
+           in0_min                in0                in0     RW    2
+           in1_min                in1                in1     RW    2
+           in2_min                in2                in2     RW    2
+           in3_min                in3                in3     RW    2
+           in4_min                in3                in3     RW    2
+           in0_max                in0                in0     RW    2
+           in1_max                in1                in1     RW    2
+           in2_max                in2                in2     RW    2
+           in3_max                in3                in3     RW    2
+           in4_max                in3                in3     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+          fan1_min               fan1               fan1     RW    0
+          fan2_min               fan2               fan2     RW    0
+              temp               NONE               NONE     R     1
+         temp_hyst               temp               temp     RW    1
+         temp_over               temp               temp     RW    1
+             temp2               NONE               NONE     R     1
+        temp2_hyst               temp               temp     RW    1
+        temp2_over               temp               temp     RW    1
+             temp3               NONE               NONE     R     1
+        temp3_hyst               temp               temp     RW    1
+        temp3_over               temp               temp     RW    1
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+            alarms               NONE               NONE     R     0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+               in0                     SENSORS_VIA686A_IN0                in0:3
+               in1                     SENSORS_VIA686A_IN1                in1:3
+               in2                     SENSORS_VIA686A_IN2                in2:3
+               in3                     SENSORS_VIA686A_IN3                in3:3
+               in4                     SENSORS_VIA686A_IN4                in4:3
+           in0_min                 SENSORS_VIA686A_IN0_MIN                in0:1
+           in1_min                 SENSORS_VIA686A_IN1_MIN                in1:1
+           in2_min                 SENSORS_VIA686A_IN2_MIN                in2:1
+           in3_min                 SENSORS_VIA686A_IN3_MIN                in3:1
+           in4_min                 SENSORS_VIA686A_IN4_MIN                in4:1
+           in0_max                 SENSORS_VIA686A_IN0_MAX                in0:2
+           in1_max                 SENSORS_VIA686A_IN1_MAX                in1:2
+           in2_max                 SENSORS_VIA686A_IN2_MAX                in2:2
+           in3_max                 SENSORS_VIA686A_IN3_MAX                in3:2
+           in4_max                 SENSORS_VIA686A_IN4_MAX                in4:2
+              fan1                    SENSORS_VIA686A_FAN1               fan1:2
+              fan2                    SENSORS_VIA686A_FAN2               fan2:2
+          fan1_min                SENSORS_VIA686A_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_VIA686A_FAN2_MIN               fan2:1
+              temp                    SENSORS_VIA686A_TEMP               temp:3
+         temp_hyst               SENSORS_VIA686A_TEMP_HYST               temp:2
+         temp_over               SENSORS_VIA686A_TEMP_OVER               temp:1
+             temp2                   SENSORS_VIA686A_TEMP2              temp2:3
+        temp2_hyst              SENSORS_VIA686A_TEMP2_HYST              temp2:2
+        temp2_over              SENSORS_VIA686A_TEMP2_OVER              temp2:1
+             temp3                   SENSORS_VIA686A_TEMP3              temp3:3
+        temp3_hyst              SENSORS_VIA686A_TEMP3_HYST              temp3:2
+        temp3_over              SENSORS_VIA686A_TEMP3_OVER              temp3:1
+          fan1_div                SENSORS_VIA686A_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_VIA686A_FAN2_DIV            fan_div:2
+            alarms                  SENSORS_VIA686A_ALARMS             alarms:1
+
+
Index: /lm-sensors/tags/V2-5-5/doc/chips/matorb
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/matorb	(revision 485)
+++ /lm-sensors/tags/V2-5-5/doc/chips/matorb	(revision 485)
@@ -0,0 +1,97 @@
+Kernel driver `matorb.o'
+=======================
+
+Status: 'Beta', but it's so simple that it may be in its final state
+
+Supported devices: 
+  * Matrix-Orbital (www.matrix-orbital.com) displays.  Developed and
+    	tested on the LCD2041V, but should work with all their displays.
+    Prefix: `matorb'
+    Addresses scanned: I2C 0x2E
+    Datasheet: Publicly available at the Matrix-Orbital web site
+    (www.matrix-orbital.com)
+
+Author: Frodo Looijaard <frodol@dds.nl>, and Philip Edelbrock
+	<phil@netroege.com>
+
+Main Contact: Philip Edelbrock <phil@netroedge.com>
+
+Module Parameters
+-----------------
+(I'm not positive about these, so don't take this as granite)
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_matorb: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `bt869' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+This driver provides access to the Matrix-Orbital display devices.  These
+devices are usually in the form of a small LCD (20x4 is typical) screen
+which is mounted in a carrier which is then mounted into a 5.25" drive bay
+of a computer.
+
+This driver allows the user to send any data to the display to control or
+display anything which the display supports.
+
+For more information on the displays or of particular features/command sets
+go to:
+
+http://www.matrix-orbital.com
+
+
+Use
+---
+To use, just redirect the base-10 ordinal value of bytes (or chars) to the
+display.  For example, to clear the screen and write a simple message, you
+could do this:
+
+# To clear the screen, send byte 0xFE and the char 'X' (ordinal 88)
+echo "254 88" > /proc/sys/dev/sensors/matorb*/disp
+
+# To display "hi there"
+echo "hi there" \
+  | perl -e '$_=<STDIN>; while (/(.)/gc) { print ord($1)," "; }' \
+  > /proc/sys/dev/sensors/matorb*/disp
+
+I've written a simple sample program (in Perl) to display a full screen
+of text (which is fed in on standard-input) to the display.  It is in the
+prog/matorb folder of this project.
+
+I think the displays are very attractive and add a neat sort of 'status'
+display to things like servers.  It would be very simple to either 
+make a script daemon (or modify the healthd provided in this project)
+to display a current status of health info (voltages, fans, temps, etc.)
+on the display.  Let's see Windoze do *THAT*! :')
+ 
+
+Issues
+------
+Redirecting too long of a string (32 ordinals seem to be the max) to 'disp'
+seems to cause problems.  Keep things in small chunks (like lines).
+
+Built in functions (screen clearing, etc.) were initially planned to be
+added as other device files (other than just 'disp'), but these functions
+are just as easily done by sending the command sequences to disp (as shown
+above when clearing the screen).  This also ensures that the driver will
+be compatible with all the displays.
+
+Detection is very poor (as of this writing, anyway).  It looks for a device
+at 0x2E and asumes it is a display.  Unfortunately, the 'beta' firmware
+the Matrix-Orbital people gave me doesn't have good detection abilities to
+it (it ignores reads... it used to lock up the bus on reads, so at least
+it doesn't do that any more! :').  If they provide an easy way to 'sense'
+that it is a display (and what kind it is?) then I will add support for it.
+
+Let me know if you have problems which you can't figure out 
+(phil@netroedge.com).
Index: /lm-sensors/tags/V2-5-5/doc/chips/lm75
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/lm75	(revision 785)
+++ /lm-sensors/tags/V2-5-5/doc/chips/lm75	(revision 785)
@@ -0,0 +1,92 @@
+Kernel driver `lm75.o'
+=====================
+
+Status: Complete and well-tested
+
+Supported chips: 
+  * National Semiconductor LM75
+    Prefix: `lm75'
+    Addresses scanned: I2C 0x48 - 0x4f (inclusive)
+    Datasheet: Publicly available at the National Semiconductor website
+               http://www.national.com
+  * National Semiconductor LM77
+    Prefix: `lm75'
+    Addresses scanned: I2C 0x48 - 0x4b (inclusive)
+    Datasheet: Publicly available at the National Semiconductor website
+               http://www.national.com
+  * Dallas Semiconductor DS75
+    Prefix: `lm75'
+    Addresses scanned: I2C 0x48 - 0x4f (inclusive)
+    Datasheet: Publicly available at the Dallas Semiconductor website
+               http://www.dalsemi.com
+  * Dallas Semiconductor DS1775
+    Prefix: `lm75'
+    Addresses scanned: I2C 0x48 - 0x4f (inclusive)
+    Datasheet: Publicly available at the Dallas Semiconductor website
+               http://www.dalsemi.com
+
+Author: Frodo Looijaard <frodol@dds.nl>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_lm75: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `lm75' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+
+The LM75 implements one temperature sensor. Limits can be set through the
+Overtemperature Shutdown register and Hysteresis register. Each value can be 
+set and read to half-degree accuracy.
+An alarm is issued (usually to a connected LM78) when the temperature
+gets higher then the Overtemperature Shutdown value; it stays on until
+the temperature falls below the Hysteresis value.
+All temperatures are in degrees Celcius, and are guaranteed within a 
+range of -55 to +125 degrees.
+
+The LM75 only updates its values each 1.5 seconds; reading it more often
+will do no harm, but will return 'old' values.
+
+The LM75 is usually used in combination with LM78-like chips, to measure
+the temperature of the processor(s).
+
+The LM77 is supported as well. It is not distinguished from an LM75
+and the two additional registers in the LM77 are not supported.
+The prefix in the /proc directory is still 'lm75'.
+
+The DS75 and DS1775 are supported as well.
+They are not distinguished from an LM75. While these chips
+have three additional bits of accuracy (12 vs. 9 for the LM75),
+the additional bits are not supported.
+The prefix in the /proc directory is still 'lm75'.
+
+
+Chip Features
+-------------
+
+Chip `lm75'
+          LABEL     LABEL CLASS   COMPUTE CLASS ACCESS MAGNITUDE
+           temp            NONE            NONE      R     1
+      temp_hyst            temp            temp      RW    1
+      temp_over            temp            temp      RW    1
+
+          LABEL                           FEATURE SYMBOL     SYSCTL FILE:OFFSET
+           temp                        SENSORS_LM75_TEMP            temp:3
+      temp_hyst                   SENSORS_LM75_TEMP_HYST            temp:2
+      temp_over                   SENSORS_LM75_TEMP_OVER            temp:1
+
+
Index: /lm-sensors/tags/V2-5-5/doc/chips/w83781d
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/w83781d	(revision 819)
+++ /lm-sensors/tags/V2-5-5/doc/chips/w83781d	(revision 819)
@@ -0,0 +1,580 @@
+Kernel driver `w83781d.o'
+=========================
+
+Status: W83781D support is complete and well-tested.
+        W83782D support is complete and well-tested.
+        W83783S support is complete but has not been well-tested.
+        W83627HF support is complete but has not been well-tested.
+        AS99127F support is BETA and has known problems due to lack of a
+		 chip datasheet. See below.
+
+Supported chips:
+  * Winbond W83781D
+    Prefix 'w83781d'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive), ISA 0x290 (8 I/O ports)
+    Datasheet: Publicly available at the Winbond website (www.winbond.com.tw)
+  * Winbond W83782D
+    Prefix 'w83782d'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive), ISA 0x290 (8 I/O ports)
+    Datasheet: Publicly available at the Winbond website
+  * Winbond W83783S
+    Prefix 'w83783s'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive)
+    Datasheet: Publicly available at the Winbond website
+  * Winbond W83627HF
+    Prefix 'w83627hf'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive), ISA 0x290 (8 I/O ports)
+    Datasheet: Publicly available at the Winbond website
+  * Asus AS99127F
+    Prefix 'as99127f'
+    Addresses scanned: I2C 0x20 - 0x2f (inclusive)
+    Datasheet: Unavailable from Asus
+
+
+Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>,
+        and Mark Studebaker <mdsxyz123@yahoo.com>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_w83781d: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `w83781d' chip
+* force_w83782d: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `w83782d' chip
+* force_w83783s: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `w83783s' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,address pairs not to scan
+* ignore_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples not to scan
+* probe: short array (min = 1, max = 48)
+  List of adapter,address pairs to scan additionally
+* probe_range: short array (min = 1, max = 48)
+  List of adapter,start-addr,end-addr triples to scan additionally
+
+
+Description
+-----------
+
+This driver implements support for the Winbond W83781D, W83782D,
+W8783S, and W83627HF chips, and the Asus AS99127F.
+We will refer to them collectively as W8378* chips.
+
+There is quite some difference between these chips, but they are similar
+enough that it was sensible to put them together in one driver. 
+The W83627HF chip is assumed to be identical to the ISA W83782D.
+The AS99127F chip is similar to an I2C-only W83782D.
+
+Detection of these chips can sometimes be foiled because they can be in
+an internal state that allows no clean access. If you know the address
+of the chip, use a 'force' parameter; this will put them into a more
+well-behaved state first.
+
+The W8378* implements temperature sensors (three on the W83781D and W83782D,
+two on the W83783S), three FAN rotation speed sensors, voltage sensors
+(seven on the W83781D, nine on the W83782D and six on the W83783S), VID
+lines, alarms with beep warnings, and some miscellaneous stuff.
+
+Temperatures are measured in degrees Celcius. There is always one main
+temperature sensor, and one (W83783S) or two (W83781D and W83782D) other
+sensors.  An alarm is triggered for the main sensor once when the 
+Overtemperature Shutdown limit is crossed; it is triggered again as soon as 
+it drops below the Hysteresis value. A more useful behaviour 
+can be found by setting the Hysteresis value to +127 degrees Celcius; in 
+this case, alarms are issued during all the time when the actual temperature
+is above the Overtemperature Shutdown value. For the other sensor(s), an
+alarm is triggered when the temperature gets higher then the Overtemperature 
+Shutdown value; it stays on until the temperature falls below the Hysteresis 
+value. But on the W83781D, there is only one alarm that functions for both 
+other sensors!  Temperatures are guaranteed within a range of -55 to +125 
+degrees. The main temperature sensors has a resolution of 1 degree; the other 
+sensor(s) of 0.5 degree.
+
+FAN rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. FAN 
+readings can be divided by a programmable divider (1, 2, 4 or 8 for the
+W83781D; 1, 2, 4, 8, 16, 32, 64 or 128 for the others) to give
+the readings more range or accuracy. This is important because some FANs
+report only one 'tick' each rotation, while others report two - making
+all readings twice as high. Not all RPM values can accurately be represented,
+so some rounding is done. With a divider of 2, the lowest representable
+value is around 2600 RPM.
+
+Voltage sensors (also known as IN sensors) report their values in volts.
+An alarm is triggered if the voltage has crossed a programmable minimum 
+or maximum limit. Note that minimum in this case always means 'closest to
+zero'; this is important for negative voltage measurements. All voltage
+inputs can measure voltages between 0 and 4.08 volts, with a resolution
+of 0.016 volt.
+
+The VID lines encode the core voltage value: the voltage level your processor 
+should work with. This is hardcoded by the mainboard and/or processor itself.
+It is a value in volts. When it is unconnected, you will often find the
+value 3.50 V here.
+
+The W83782D and W83783S temperature conversion machine understands about
+several kinds of temperature probes. You can program the so-called
+beta value in the sensor files. '1' is the PII/Celeron diode, '2' is the
+TN3904 transistor, and 3435 the default thermistor value. Other values
+are (not yet) supported. You can program your own values through the
+rt interface, but this is not very useful, because these tables are
+under NDA.
+
+In addition to the alarms described above, there is a CHAS alarm on the
+chips which triggers if your computer case is open. 
+
+When an alarm goes off, you can be warned by a beeping signal through
+your computer speaker. It is possible to enable all beeping globally,
+or only the beeping for some alarms.
+
+If an alarm triggers, it will remain triggered until the hardware register
+is read at least once. This means that the cause for the alarm may 
+already have disappeared!  Note that in the current implementation, all
+hardware registers are read whenever any data is read (unless it is less
+than 1.5 seconds since the last update). This means that you can easily
+miss once-only alarms.
+
+The W8378* only updates its values each 1.5 seconds; reading it more often
+will do no harm, but will return 'old' values.
+
+
+AS99127F PROBLEMS
+-----------------
+The as99127f support was developed without the benefit of a datasheet.
+In most cases it is treated as a w83781d.
+This support is BETA. One user has reported problems with fans stopping
+occasionally.
+
+Known problems:
+	- -12V readings a little high (-9.5V to -10.5V)
+	- Problems with diode/thermistor settings (supported?)
+	- fan_div register may not really be fan_div.
+	- One user reports fans stopping under high server load.
+
+These will not be fixed unless we get a datasheet.
+If you have problems, please lobby Asus to release a datasheet.
+
+Note that the individual beep bits are inverted from the other chips.
+That is, in the second entry in the 'beep' file, a '1' bit means
+disable, not enable.
+
+
+Chip Features
+-------------
+
+Chip `w83781d'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+               in0               NONE               NONE     R     2
+               in1               NONE               NONE     R     2
+               in2               NONE               NONE     R     2
+               in3               NONE               NONE     R     2
+               in4               NONE               NONE     R     2
+               in5               NONE               NONE     R     2
+               in6               NONE               NONE     R     2
+           in0_min                in0                in0     RW    2
+           in1_min                in1                in1     RW    2
+           in2_min                in2                in2     RW    2
+           in3_min                in3                in3     RW    2
+           in4_min                in4                in4     RW    2
+           in5_min                in5                in5     RW    2
+           in6_min                in6                in6     RW    2
+           in0_max                in0                in0     RW    2
+           in1_max                in1                in1     RW    2
+           in2_max                in2                in2     RW    2
+           in3_max                in3                in3     RW    2
+           in4_max                in4                in4     RW    2
+           in5_max                in5                in5     RW    2
+           in6_max                in6                in6     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+              fan3               NONE               NONE     R     0
+          fan1_min               fan1               NONE     RW    0
+          fan2_min               fan2               NONE     RW    0
+          fan3_min               fan3               NONE     RW    0
+             temp1               NONE               NONE     R     1
+        temp1_hyst              temp1              temp1     RW    1
+        temp1_over              temp1              temp1     RW    1
+             temp2               NONE               NONE     R     1
+        temp2_hyst              temp2              temp2     RW    1
+        temp2_over              temp2              temp2     RW    1
+             temp3               NONE               NONE     R     1
+        temp3_hyst              temp3              temp3     RW    1
+        temp3_over              temp3              temp3     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+          fan3_div               fan3               NONE     R     0
+            alarms               NONE               NONE     R     0
+       beep_enable             alarms               NONE     RW    0
+             beeps             alarms             alarms     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+               in0                     SENSORS_W83781D_IN0                in0:3
+               in1                     SENSORS_W83781D_IN1                in1:3
+               in2                     SENSORS_W83781D_IN2                in2:3
+               in3                     SENSORS_W83781D_IN3                in3:3
+               in4                     SENSORS_W83781D_IN4                in4:3
+               in5                     SENSORS_W83781D_IN5                in5:3
+               in6                     SENSORS_W83781D_IN6                in6:3
+           in0_min                 SENSORS_W83781D_IN0_MIN                in0:1
+           in1_min                 SENSORS_W83781D_IN1_MIN                in1:1
+           in2_min                 SENSORS_W83781D_IN2_MIN                in2:1
+           in3_min                 SENSORS_W83781D_IN3_MIN                in3:1
+           in4_min                 SENSORS_W83781D_IN4_MIN                in4:1
+           in5_min                 SENSORS_W83781D_IN5_MIN                in5:1
+           in6_min                 SENSORS_W83781D_IN6_MIN                in6:1
+           in0_max                 SENSORS_W83781D_IN0_MAX                in0:2
+           in1_max                 SENSORS_W83781D_IN1_MAX                in1:2
+           in2_max                 SENSORS_W83781D_IN2_MAX                in2:2
+           in3_max                 SENSORS_W83781D_IN3_MAX                in3:2
+           in4_max                 SENSORS_W83781D_IN4_MAX                in4:2
+           in5_max                 SENSORS_W83781D_IN5_MAX                in5:2
+           in6_max                 SENSORS_W83781D_IN6_MAX                in6:2
+              fan1                    SENSORS_W83781D_FAN1               fan1:2
+              fan2                    SENSORS_W83781D_FAN2               fan2:2
+              fan3                    SENSORS_W83781D_FAN3               fan3:2
+          fan1_min                SENSORS_W83781D_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_W83781D_FAN2_MIN               fan2:1
+          fan3_min                SENSORS_W83781D_FAN3_MIN               fan3:1
+             temp1                   SENSORS_W83781D_TEMP1              temp1:3
+        temp1_hyst              SENSORS_W83781D_TEMP1_HYST              temp1:2
+        temp1_over              SENSORS_W83781D_TEMP1_OVER              temp1:1
+             temp2                   SENSORS_W83781D_TEMP2              temp2:3
+        temp2_hyst              SENSORS_W83781D_TEMP2_HYST              temp2:2
+        temp2_over              SENSORS_W83781D_TEMP2_OVER              temp2:1
+             temp3                   SENSORS_W83781D_TEMP3              temp3:3
+        temp3_hyst              SENSORS_W83781D_TEMP3_HYST              temp3:2
+        temp3_over              SENSORS_W83781D_TEMP3_OVER              temp3:1
+               vid                     SENSORS_W83781D_VID                vid:1
+          fan1_div                SENSORS_W83781D_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_W83781D_FAN2_DIV            fan_div:2
+          fan3_div                SENSORS_W83781D_FAN3_DIV            fan_div:3
+            alarms                  SENSORS_W83781D_ALARMS             alarms:1
+       beep_enable             SENSORS_W83781D_BEEP_ENABLE               beep:1
+             beeps                   SENSORS_W83781D_BEEPS               beep:2
+
+
+Chip `w83782d'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+               in0               NONE               NONE     R     2
+               in1               NONE               NONE     R     2
+               in2               NONE               NONE     R     2
+               in3               NONE               NONE     R     2
+               in4               NONE               NONE     R     2
+               in5               NONE               NONE     R     2
+               in6               NONE               NONE     R     2
+               in7               NONE               NONE     R     2
+               in8               NONE               NONE     R     2
+           in0_min                in0                in0     RW    2
+           in1_min                in1                in1     RW    2
+           in2_min                in2                in2     RW    2
+           in3_min                in3                in3     RW    2
+           in4_min                in4                in4     RW    2
+           in5_min                in5                in5     RW    2
+           in6_min                in6                in6     RW    2
+           in7_min                in7                in7     RW    2
+           in8_min                in8                in8     RW    2
+           in0_max                in0                in0     RW    2
+           in1_max                in1                in1     RW    2
+           in2_max                in2                in2     RW    2
+           in3_max                in3                in3     RW    2
+           in4_max                in4                in4     RW    2
+           in5_max                in5                in5     RW    2
+           in6_max                in6                in6     RW    2
+           in7_max                in7                in7     RW    2
+           in8_max                in8                in8     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+              fan3               NONE               NONE     R     0
+          fan1_min               fan1               NONE     RW    0
+          fan2_min               fan2               NONE     RW    0
+          fan3_min               fan3               NONE     RW    0
+             temp1               NONE               NONE     R     1
+        temp1_hyst              temp1              temp1     RW    1
+        temp1_over              temp1              temp1     RW    1
+             temp2               NONE               NONE     R     1
+        temp2_hyst              temp2              temp2     RW    1
+        temp2_over              temp2              temp2     RW    1
+             temp3               NONE               NONE     R     1
+        temp3_hyst              temp3              temp3     RW    1
+        temp3_over              temp3              temp3     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+          fan3_div               fan3               NONE     R     0
+            alarms               NONE               NONE     R     0
+       beep_enable             alarms               NONE     RW    0
+             beeps             alarms             alarms     RW    0
+           sensor1               NONE               NONE     RW    0
+           sensor2               NONE               NONE     RW    0
+           sensor3               NONE               NONE     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+               in0                     SENSORS_W83782D_IN0                in0:3
+               in1                     SENSORS_W83782D_IN1                in1:3
+               in2                     SENSORS_W83782D_IN2                in2:3
+               in3                     SENSORS_W83782D_IN3                in3:3
+               in4                     SENSORS_W83782D_IN4                in4:3
+               in5                     SENSORS_W83782D_IN5                in5:3
+               in6                     SENSORS_W83782D_IN6                in6:3
+               in7                     SENSORS_W83782D_IN7                in7:3
+               in8                     SENSORS_W83782D_IN8                in8:3
+           in0_min                 SENSORS_W83782D_IN0_MIN                in0:1
+           in1_min                 SENSORS_W83782D_IN1_MIN                in1:1
+           in2_min                 SENSORS_W83782D_IN2_MIN                in2:1
+           in3_min                 SENSORS_W83782D_IN3_MIN                in3:1
+           in4_min                 SENSORS_W83782D_IN4_MIN                in4:1
+           in5_min                 SENSORS_W83782D_IN5_MIN                in5:1
+           in6_min                 SENSORS_W83782D_IN6_MIN                in6:1
+           in7_min                 SENSORS_W83782D_IN7_MIN                in7:1
+           in8_min                 SENSORS_W83782D_IN8_MIN                in8:1
+           in0_max                 SENSORS_W83782D_IN0_MAX                in0:2
+           in1_max                 SENSORS_W83782D_IN1_MAX                in1:2
+           in2_max                 SENSORS_W83782D_IN2_MAX                in2:2
+           in3_max                 SENSORS_W83782D_IN3_MAX                in3:2
+           in4_max                 SENSORS_W83782D_IN4_MAX                in4:2
+           in5_max                 SENSORS_W83782D_IN5_MAX                in5:2
+           in6_max                 SENSORS_W83782D_IN6_MAX                in6:2
+           in7_max                 SENSORS_W83782D_IN7_MAX                in7:2
+           in8_max                 SENSORS_W83782D_IN8_MAX                in8:2
+              fan1                    SENSORS_W83782D_FAN1               fan1:2
+              fan2                    SENSORS_W83782D_FAN2               fan2:2
+              fan3                    SENSORS_W83781D_FAN3               fan3:2
+          fan1_min                SENSORS_W83782D_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_W83782D_FAN2_MIN               fan2:1
+          fan3_min                SENSORS_W83781D_FAN3_MIN               fan3:1
+             temp1                   SENSORS_W83782D_TEMP1              temp1:3
+        temp1_hyst              SENSORS_W83782D_TEMP1_HYST              temp1:2
+        temp1_over              SENSORS_W83782D_TEMP1_OVER              temp1:1
+             temp2                   SENSORS_W83782D_TEMP2              temp2:3
+        temp2_hyst              SENSORS_W83782D_TEMP2_HYST              temp2:2
+        temp2_over              SENSORS_W83782D_TEMP2_OVER              temp2:1
+             temp3                   SENSORS_W83782D_TEMP3              temp3:3
+        temp3_hyst              SENSORS_W83782D_TEMP3_HYST              temp3:2
+        temp3_over              SENSORS_W83782D_TEMP3_OVER              temp3:1
+               vid                     SENSORS_W83782D_VID                vid:1
+          fan1_div                SENSORS_W83782D_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_W83782D_FAN2_DIV            fan_div:2
+          fan3_div                SENSORS_W83781D_FAN3_DIV            fan_div:3
+            alarms                  SENSORS_W83782D_ALARMS             alarms:1
+       beep_enable             SENSORS_W83782D_BEEP_ENABLE               beep:1
+             beeps                   SENSORS_W83782D_BEEPS               beep:2
+           sensor1                   SENSORS_W83782D_SENS1            sensor1:1
+           sensor2                   SENSORS_W83782D_SENS2            sensor2:1
+           sensor3                   SENSORS_W83782D_SENS3            sensor3:1
+
+
+Chip `w83783s'
+             LABEL        LABEL CLASS      COMPUTE CLASS ACCESS MAGNITUDE
+               in0               NONE               NONE     R     2
+               in1               NONE               NONE     R     2
+               in2               NONE               NONE     R     2
+               in3               NONE               NONE     R     2
+               in4               NONE               NONE     R     2
+               in5               NONE               NONE     R     2
+               in6               NONE               NONE     R     2
+           in0_min                in0                in0     RW    2
+           in1_min                in1                in1     RW    2
+           in2_min                in2                in2     RW    2
+           in3_min                in3                in3     RW    2
+           in4_min                in4                in4     RW    2
+           in5_min                in5                in5     RW    2
+           in6_min                in6                in6     RW    2
+           in0_max                in0                in0     RW    2
+           in1_max                in1                in1     RW    2
+           in2_max                in2                in2     RW    2
+           in3_max                in3                in3     RW    2
+           in4_max                in4                in4     RW    2
+           in5_max                in5                in5     RW    2
+           in6_max                in6                in6     RW    2
+              fan1               NONE               NONE     R     0
+              fan2               NONE               NONE     R     0
+              fan3               NONE               NONE     R     0
+          fan1_min               fan1               NONE     RW    0
+          fan2_min               fan2               NONE     RW    0
+          fan3_min               fan3               NONE     RW    0
+             temp1               NONE               NONE     R     1
+        temp1_hyst              temp1              temp1     RW    1
+        temp1_over              temp1              temp1     RW    1
+             temp2               NONE               NONE     R     1
+        temp2_hyst              temp2              temp2     RW    1
+        temp2_over              temp2              temp2     RW    1
+               vid               NONE               NONE     R     2
+          fan1_div               fan1               NONE     RW    0
+          fan2_div               fan2               NONE     RW    0
+          fan3_div               fan3               NONE     R     0
+            alarms               NONE               NONE     R     0
+       beep_enable             alarms               NONE     RW    0
+             beeps             alarms             alarms     RW    0
+           sensor1               NONE               NONE     RW    0
+           sensor2               NONE               NONE     RW    0
+
+             LABEL                          FEATURE SYMBOL        SYSCTL FILE:NR
+               in0                     SENSORS_W83783S_IN0                in0:3
+               in1                     SENSORS_W83783S_IN1                in1:3
+               in2                     SENSORS_W83783S_IN2                in2:3
+               in3                     SENSORS_W83783S_IN3                in3:3
+               in4                     SENSORS_W83783S_IN4                in4:3
+               in5                     SENSORS_W83783S_IN5                in5:3
+               in6                     SENSORS_W83783S_IN6                in6:3
+           in0_min                 SENSORS_W83783S_IN0_MIN                in0:1
+           in1_min                 SENSORS_W83783S_IN1_MIN                in1:1
+           in2_min                 SENSORS_W83783S_IN2_MIN                in2:1
+           in3_min                 SENSORS_W83783S_IN3_MIN                in3:1
+           in4_min                 SENSORS_W83783S_IN4_MIN                in4:1
+           in5_min                 SENSORS_W83783S_IN5_MIN                in5:1
+           in6_min                 SENSORS_W83783S_IN6_MIN                in6:1
+           in0_max                 SENSORS_W83783S_IN0_MAX                in0:2
+           in1_max                 SENSORS_W83783S_IN1_MAX                in1:2
+           in2_max                 SENSORS_W83783S_IN2_MAX                in2:2
+           in3_max                 SENSORS_W83783S_IN3_MAX                in3:2
+           in4_max                 SENSORS_W83783S_IN4_MAX                in4:2
+           in5_max                 SENSORS_W83783S_IN5_MAX                in5:2
+           in6_max                 SENSORS_W83783S_IN6_MAX                in6:2
+              fan1                    SENSORS_W83783S_FAN1               fan1:2
+              fan2                    SENSORS_W83783S_FAN2               fan2:2
+              fan3                    SENSORS_W83783S_FAN3               fan3:2
+          fan1_min                SENSORS_W83783S_FAN1_MIN               fan1:1
+          fan2_min                SENSORS_W83783S_FAN2_MIN               fan2:1
+          fan3_min                SENSORS_W83783S_FAN3_MIN               fan3:1
+             temp1                   SENSORS_W83783S_TEMP1              temp1:3
+        temp1_hyst              SENSORS_W83783S_TEMP1_HYST              temp1:2
+        temp1_over              SENSORS_W83783S_TEMP1_OVER              temp1:1
+             temp2                   SENSORS_W83783S_TEMP2              temp2:3
+        temp2_hyst              SENSORS_W83783S_TEMP2_HYST              temp2:2
+        temp2_over              SENSORS_W83783S_TEMP2_OVER              temp2:1
+               vid                     SENSORS_W83783S_VID                vid:1
+          fan1_div                SENSORS_W83783S_FAN1_DIV            fan_div:1
+          fan2_div                SENSORS_W83783S_FAN2_DIV            fan_div:2
+          fan3_div                SENSORS_W83783S_FAN3_DIV            fan_div:3
+            alarms                  SENSORS_W83783S_ALARMS             alarms:1
+       beep_enable             SENSORS_W83783S_BEEP_ENABLE               beep:1
+             beeps                   SENSORS_W83783S_BEEPS               beep:2
+           sensor1                   SENSORS_W83783S_SENS1            sensor1:1
+           sensor2                   SENSORS_W83783S_SENS2            sensor2:1
+
+
+
+Miscellaneous documentation
+===========================
+
+
+
+/PROC ENTRIES
+-------------
+Varies depending on chip type. Most are identical to the lm78 entries.
+See the lm78 documentation for details.
+
+Additional entries not in the lm78:
+
+	pwm[1-4]: (782d/783s only)
+	  Controls the speed of the fans with PWM (Pulse Width Modulation)
+	  Valid values:
+	    0 - 255. 255 = max speed.
+
+	sensor[1-3]: (782d/783s only)
+	  Controls the sensor type. To change to a different
+	  sensor type, for example, do 'echo 2 > sensor1'.
+	  Valid values:
+	       1: Pentium II / Celeron diode
+	       2: 2N3904 Transistor in a diode configuration
+	    3435: Thermistor with Beta = 3435. Beta is a measure
+	          of sensitivity to temperature.
+	  Thermistors with other Betas could in theory be supported
+	  after correspondence with Winbond. See below.
+
+	rt[1-3]: (781d only)
+	  32-byte tables controlling the conversion from
+	  resistance to temperature for thermistors.
+	  For the curious only. Writing to these entries will
+	  probably render temperature sensing insane.
+	  Only set at initialization; not updated every 1.5 seconds.
+
+
+
+TO DO:
+-----
+ 782d programmable pins
+
+
+NOTES:
+-----
+  783s has no in1 so that in[2-6] are compatible with the 781d/782d.
+
+  783s pin is programmable for -5V or temp1; defaults to -5V,
+       no control in driver so temp1 doesn't work.
+
+  782d and 783s datasheets differ on which is pwm1 and which is pwm2.
+       We chose to follow 782d.
+
+  782d and 783s pin is programmable for fan3 input or pwm2 output;
+       defaults to fan3 input.
+       no control in driver so pwm2 doesn't work.
+
+  782d has pwm1-2 for isa, pwm1-4 for i2c. (pwm3-4 share pins with
+       the isa pins)
+
+  781d allowable values for fan_div are 1,2,4,8.
+  782d/783s allowable values for fan_div are 1,2,4,8,16,32,64,128.
+
+
+
+Data sheet updates:
+------------------
+	- Mask off lower bit of all ID registers.
+
+	- PWM clock registers:
+
+		000: master /  512
+		001: master / 1024
+		010: master / 2048
+		011: master / 4096
+		100: master / 8192
+
+
+Answers from Winbond tech support
+---------------------------------
+> 
+> 1) In the W83781D data sheet section 7.2 last paragraph, it talks about
+>    reprogramming the R-T table if the Beta of the thermistor is not
+>    3435K. The R-T table is described briefly in section 8.20.
+>    What formulas do I use to program a new R-T table for a given Beta?
+> 
+	We are sorry that the calculation for R-T table value is
+confidential. If you have another Beta value of thermistor, we can help
+to calculate the R-T table for you. But you should give us real R-T
+Table which can be gotten by thermistor vendor. Therefore we will calculate
+them and obtain 32-byte data, and you can fill the 32-byte data to the
+register in Bank0.CR51 of W83781D.
+
+
+> 2) In the W83782D data sheet, it mentions that pins 38, 39, and 40 are 
+>    programmable to be either thermistor or Pentium II diode inputs. 
+>    How do I program them for diode inputs? I can't find any register
+>    to program these to be diode inputs.
+ --> You may program Bank0 CR[5Dh] and CR[59h] registers.
+ 
+ 	CR[5Dh]    		bit 1(VTIN1)    bit 2(VTIN2)   bit 3(VTIN3)
+ 	      
+      	thermistor                0		 0		0	
+ 	diode 		          1		 1		1		
+ 
+ 
+(error) CR[59h] 		bit 4(VTIN1)	bit 2(VTIN2)   bit 3(VTIN3)
+(right) CR[59h] 		bit 4(VTIN1)	bit 5(VTIN2)   bit 6(VTIN3)
+ 	
+ 	PII thermal diode         1		 1		1
+ 	2N3904	diode	          0		 0		0
+ 
+ 
+Author/Maintainer
+-----------------
+Original 781D support: Frodo and Phil (sensors@stimpy.netroedge.com)
+782D/783S support added by: Mark D. Studebaker (mdsxyz123@yahoo.com)
+Send requests to the sensors@stimpy.netroedge.com mailing list.
Index: /lm-sensors/tags/V2-5-5/doc/chips/sis5595
===================================================================
--- /lm-sensors/tags/V2-5-5/doc/chips/sis5595	(revision 746)
+++ /lm-sensors/tags/V2-5-5/doc/chips/sis5595	(revision 746)
@@ -0,0 +1,134 @@
+Kernel driver `sis5595.o'
+=========================
+
+Status: Complete and tested
+
+Supported chips:
+  * Silicon Integrated Systems Corp. SiS5595 Southbridge Hardware Monitor
+    Prefix: sis5595
+    Addresses scanned: ISA in PCI-space encoded address
+    Datasheet: Publicly available at the Silicon Integrated Systems Corp. site.
+
+Author: Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+
+Module Parameters
+-----------------
+
+* force: short array (min = 1, max = 48)
+  List of adapter,address pairs to boldly assume to be present
+* force_sis5595: short array (min = 1, max = 48)
+  List of adapter,address pairs which are unquestionably assumed to contain
+  a `sis5595' chip
+* ignore: short array (min = 1, max = 48)
+  List of adapter,a