aboutsummaryrefslogtreecommitdiffstats
path: root/i2c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2006-04-25 15:26:28 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-04-25 15:26:28 -0700
commit393db0324a430e100971ead4210d1c7870509b7c (patch)
tree10a2cded9e03b7ea51fdac154328cde938e411aa /i2c
parentefe9e8261c1359bdb9ae90661eff88c8e740d3f9 (diff)
downloadpatches-393db0324a430e100971ead4210d1c7870509b7c.tar.gz
more patches added
Diffstat (limited to 'i2c')
-rw-r--r--i2c/hwmon-hdaps-update-id-list.patch55
-rw-r--r--i2c/hwmon-improve-Kconfig-help.patch78
-rw-r--r--i2c/hwmon-lm83-documentation-update.patch28
-rw-r--r--i2c/hwmon-vid-mask-per-vrm.patch69
-rw-r--r--i2c/hwmon-w83791d-new-driver.patch1429
-rw-r--r--i2c/i2c-nforce2-add-mcp51-mcp55-support.patch131
-rw-r--r--i2c/i2c-piix4-fix-typo-in-documentation.patch28
-rw-r--r--i2c/i2c-piix4-improve-ibm-error-message.patch54
-rw-r--r--i2c/i2c-piix4-remove-fix_hstcfg-parameter.patch116
-rw-r--r--i2c/rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch505
10 files changed, 2493 insertions, 0 deletions
diff --git a/i2c/hwmon-hdaps-update-id-list.patch b/i2c/hwmon-hdaps-update-id-list.patch
new file mode 100644
index 0000000000000..7c9ba948d06d8
--- /dev/null
+++ b/i2c/hwmon-hdaps-update-id-list.patch
@@ -0,0 +1,55 @@
+From khali@linux-fr.org Tue Apr 25 05:20:08 2006
+Date: Tue, 25 Apr 2006 14:20:11 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Cc: Arkadiusz Miskiewicz <arekm@maven.pl>, Frank Gevaerts <frank@gevaerts.be>
+Subject: HWMON: hdaps: Update the list of supported systems
+Message-Id: <20060425142011.6dbc8a14.khali@linux-fr.org>
+Content-Disposition: inline; filename=hwmon-hdaps-update-id-list.patch
+
+Update the list of systems supported by the hdaps driver:
+* Add the "ThinkPad Z60m" entry, reported by Arkadiusz Miskiewicz.
+* Add the "ThinkPad H" entry, reported by Frank Gevaerts for some
+ ThinkPad R52 models (1846AQG).
+* Drop the "ThinkPad X41 Tablet" entry, which looks redundant to me.
+And a comment update for good measure.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Cc: Arkadiusz Miskiewicz <arekm@maven.pl>
+Cc: Frank Gevaerts <frank@gevaerts.be>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/hwmon/hdaps.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- gregkh-2.6.orig/drivers/hwmon/hdaps.c
++++ gregkh-2.6/drivers/hwmon/hdaps.c
+@@ -522,13 +522,15 @@ static int __init hdaps_init(void)
+ {
+ int ret;
+
+- /* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */
++ /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
++ "ThinkPad T42p", so the order of the entries matters */
+ struct dmi_system_id hdaps_whitelist[] = {
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad H"),
+ HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"),
++ HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */
+ HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"),
+ HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"),
+@@ -536,9 +538,9 @@ static int __init hdaps_init(void)
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
+ HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
+- HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"),
+ HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
+ HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
++ HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"),
+ { .ident = NULL }
+ };
+
diff --git a/i2c/hwmon-improve-Kconfig-help.patch b/i2c/hwmon-improve-Kconfig-help.patch
new file mode 100644
index 0000000000000..8849b4917aa9c
--- /dev/null
+++ b/i2c/hwmon-improve-Kconfig-help.patch
@@ -0,0 +1,78 @@
+From khali@linux-fr.org Tue Apr 25 05:23:01 2006
+Date: Tue, 25 Apr 2006 14:23:01 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Subject: HWMON: Improve the help text for CONFIG_HWMON
+Message-Id: <20060425142301.1981401b.khali@linux-fr.org>
+Content-Disposition: inline; filename=hwmon-improve-Kconfig-help.patch
+
+Improve the help text for CONFIG_HWMON to let the users know how they
+pick the right hardware monitoring driver(s) for their system.
+
+Also fix a couple typos in the related documentation file and improve
+some parts a bit.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/hwmon/userspace-tools | 17 +++++++++--------
+ drivers/hwmon/Kconfig | 4 ++++
+ 2 files changed, 13 insertions(+), 8 deletions(-)
+
+--- gregkh-2.6.orig/Documentation/hwmon/userspace-tools
++++ gregkh-2.6/Documentation/hwmon/userspace-tools
+@@ -6,31 +6,32 @@ voltages, fans speed). They are often co
+ are also connected directly through the ISA bus.
+
+ The kernel drivers make the data from the sensor chips available in the /sys
+-virtual filesystem. Userspace tools are then used to display or set or the
+-data in a more friendly manner.
++virtual filesystem. Userspace tools are then used to display the measured
++values or configure the chips in a more friendly manner.
+
+ Lm-sensors
+ ----------
+
+-Core set of utilites that will allow you to obtain health information,
++Core set of utilities that will allow you to obtain health information,
+ setup monitoring limits etc. You can get them on their homepage
+ http://www.lm-sensors.nu/ or as a package from your Linux distribution.
+
+ If from website:
+-Get lmsensors from project web site. Please note, you need only userspace
+-part, so compile with "make user_install" target.
++Get lm-sensors from project web site. Please note, you need only userspace
++part, so compile with "make user" and install with "make user_install".
+
+ General hints to get things working:
+
+ 0) get lm-sensors userspace utils
+-1) compile all drivers in I2C section as modules in your kernel
++1) compile all drivers in I2C and Hardware Monitoring sections as modules
++ in your kernel
+ 2) run sensors-detect script, it will tell you what modules you need to load.
+ 3) load them and run "sensors" command, you should see some results.
+ 4) fix sensors.conf, labels, limits, fan divisors
+ 5) if any more problems consult FAQ, or documentation
+
+-Other utilites
+---------------
++Other utilities
++---------------
+
+ If you want some graphical indicators of system health look for applications
+ like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd,
+--- gregkh-2.6.orig/drivers/hwmon/Kconfig
++++ gregkh-2.6/drivers/hwmon/Kconfig
+@@ -16,6 +16,10 @@ config HWMON
+ should say Y here and also to the specific driver(s) for your
+ sensors chip(s) below.
+
++ To find out which specific driver(s) you need, use the
++ sensors-detect script from the lm_sensors package. Read
++ <file:Documentation/hwmon/userspace-tools> for details.
++
+ This support can also be built as a module. If so, the module
+ will be called hwmon.
+
diff --git a/i2c/hwmon-lm83-documentation-update.patch b/i2c/hwmon-lm83-documentation-update.patch
new file mode 100644
index 0000000000000..c2f638ff1c9d3
--- /dev/null
+++ b/i2c/hwmon-lm83-documentation-update.patch
@@ -0,0 +1,28 @@
+From khali@linux-fr.org Tue Apr 25 05:22:12 2006
+Date: Tue, 25 Apr 2006 14:22:14 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Subject: HWMON: lm83: Documentation update
+Message-Id: <20060425142214.1ff4b7b2.khali@linux-fr.org>
+Content-Disposition: inline; filename=hwmon-lm83-documentation-update.patch
+
+One more motherboard confirmed to have an LM83 temperature sensor chip.
+Thanks to Steven Hardy for reporting.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/hwmon/lm83 | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- gregkh-2.6.orig/Documentation/hwmon/lm83
++++ gregkh-2.6/Documentation/hwmon/lm83
+@@ -35,6 +35,7 @@ contact us. Note that the LM90 can easil
+
+ Confirmed motherboards:
+ SBS P014
++ SBS PSL09
+
+ Unconfirmed motherboards:
+ Gigabyte GA-8IK1100
diff --git a/i2c/hwmon-vid-mask-per-vrm.patch b/i2c/hwmon-vid-mask-per-vrm.patch
new file mode 100644
index 0000000000000..fc23c5248897e
--- /dev/null
+++ b/i2c/hwmon-vid-mask-per-vrm.patch
@@ -0,0 +1,69 @@
+From khali@linux-fr.org Tue Apr 25 05:24:44 2006
+Date: Tue, 25 Apr 2006 14:24:46 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Cc: Rudolf Marek <r.marek@sh.cvut.cz>
+Subject: HWMON: Trim VID values to correct number of bits
+Message-Id: <20060425142446.4efec6a6.khali@linux-fr.org>
+Content-Disposition: inline; filename=hwmon-vid-mask-per-vrm.patch
+
+From: Rudolf Marek <r.marek@sh.cvut.cz>
+
+Following patch trims the VID value to correct number of bits
+for each VRM.
+
+Signed-off-by: Rudolf Marek <r.marek@sh.cvut.cz>
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/hwmon/hwmon-vid.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/drivers/hwmon/hwmon-vid.c
++++ gregkh-2.6/drivers/hwmon/hwmon-vid.c
+@@ -70,6 +70,7 @@ int vid_from_reg(int val, u8 vrm)
+ switch(vrm) {
+
+ case 100: /* VRD 10.0 */
++ val &= 0x3f;
+ if((val & 0x1f) == 0x1f)
+ return 0;
+ if((val & 0x1f) <= 0x09 || val == 0x0a)
+@@ -82,14 +83,17 @@ int vid_from_reg(int val, u8 vrm)
+ return vid;
+
+ case 24: /* Opteron processor */
++ val &= 0x1f;
+ return(val == 0x1f ? 0 : 1550 - val * 25);
+
+ case 91: /* VRM 9.1 */
+ case 90: /* VRM 9.0 */
++ val &= 0x1f;
+ return(val == 0x1f ? 0 :
+ 1850 - val * 25);
+
+ case 85: /* VRM 8.5 */
++ val &= 0x1f;
+ return((val & 0x10 ? 25 : 0) +
+ ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+ ((val & 0x0f) * 50));
+@@ -98,14 +102,17 @@ int vid_from_reg(int val, u8 vrm)
+ val &= 0x0f;
+ /* fall through */
+ case 82: /* VRM 8.2 */
++ val &= 0x1f;
+ return(val == 0x1f ? 0 :
+ val & 0x10 ? 5100 - (val) * 100 :
+ 2050 - (val) * 50);
+ case 17: /* Intel IMVP-II */
++ val &= 0x1f;
+ return(val & 0x10 ? 975 - (val & 0xF) * 25 :
+ 1750 - val * 50);
+ case 13:
+- return(1708 - (val & 0x3f) * 16);
++ val &= 0x3f;
++ return(1708 - val * 16);
+ default: /* report 0 for unknown */
+ printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n");
+ return 0;
diff --git a/i2c/hwmon-w83791d-new-driver.patch b/i2c/hwmon-w83791d-new-driver.patch
new file mode 100644
index 0000000000000..a886e7c9aa522
--- /dev/null
+++ b/i2c/hwmon-w83791d-new-driver.patch
@@ -0,0 +1,1429 @@
+From khali@linux-fr.org Tue Apr 25 05:21:01 2006
+Date: Tue, 25 Apr 2006 14:21:03 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Cc: Charles Spirakis <bezaur@gmail.com>
+Subject: HWMON: w83791d: New hardware monitoring driver for the Winbond W83791D
+Message-Id: <20060425142103.28daa195.khali@linux-fr.org>
+Content-Disposition: inline; filename=hwmon-w83791d-new-driver.patch
+
+From: Charles Spirakis <bezaur@gmail.com>
+
+Add support for the w83791d sensor chip. The w83791d hardware is
+somewhere between the w83781d and the w83792d and this driver code
+is derived from the code that supports those chips.
+
+Signed-off-by: Charles Spirakis <bezaur@gmail.com>
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/hwmon/w83791d | 113 +++
+ drivers/hwmon/Kconfig | 10
+ drivers/hwmon/Makefile | 1
+ drivers/hwmon/w83791d.c | 1255 ++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1379 insertions(+)
+
+--- /dev/null
++++ gregkh-2.6/Documentation/hwmon/w83791d
+@@ -0,0 +1,113 @@
++Kernel driver w83791d
++=====================
++
++Supported chips:
++ * Winbond W83791D
++ Prefix: 'w83791d'
++ Addresses scanned: I2C 0x2c - 0x2f
++ Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83791Da.pdf
++
++Author: Charles Spirakis <bezaur@gmail.com>
++
++This driver was derived from the w83781d.c and w83792d.c source files.
++
++Credits:
++ w83781d.c:
++ Frodo Looijaard <frodol@dds.nl>,
++ Philip Edelbrock <phil@netroedge.com>,
++ and Mark Studebaker <mdsxyz123@yahoo.com>
++ w83792d.c:
++ Chunhao Huang <DZShen@Winbond.com.tw>,
++ Rudolf Marek <r.marek@sh.cvut.cz>
++
++Module Parameters
++-----------------
++
++* init boolean
++ (default 0)
++ Use 'init=1' to have the driver do extra software initializations.
++ The default behavior is to do the minimum initialization possible
++ and depend on the BIOS to properly setup the chip. If you know you
++ have a w83791d and you're having problems, try init=1 before trying
++ reset=1.
++
++* reset boolean
++ (default 0)
++ Use 'reset=1' to reset the chip (via index 0x40, bit 7). The default
++ behavior is no chip reset to preserve BIOS settings.
++
++* force_subclients=bus,caddr,saddr,saddr
++ This is used to force the i2c addresses for subclients of
++ a certain chip. Example usage is `force_subclients=0,0x2f,0x4a,0x4b'
++ to force the subclients of chip 0x2f on bus 0 to i2c addresses
++ 0x4a and 0x4b.
++
++
++Description
++-----------
++
++This driver implements support for the Winbond W83791D chip.
++
++Detection of the chip can sometimes be foiled because it can be in an
++internal state that allows no clean access (Bank with ID register is not
++currently selected). If you know the address of the chip, use a 'force'
++parameter; this will put it into a more well-behaved state first.
++
++The driver implements three temperature sensors, five fan rotation speed
++sensors, and ten voltage sensors.
++
++Temperatures are measured in degrees Celsius and measurement resolution is 1
++degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
++the temperature gets higher than the Overtemperature Shutdown value; it stays
++on until the temperature falls below the Hysteresis value.
++
++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, 8 for fan 1/2/3
++and 1, 2, 4, 8, 16, 32, 64 or 128 for fan 4/5) to give the readings more
++range or accuracy.
++
++Voltage sensors (also known as IN sensors) report their values in millivolts.
++An alarm is triggered if the voltage has crossed a programmable minimum
++or maximum limit.
++
++Alarms are provided as output from a "realtime status register". The
++following bits are defined:
++
++bit - alarm on:
++0 - Vcore
++1 - VINR0
++2 - +3.3VIN
++3 - 5VDD
++4 - temp1
++5 - temp2
++6 - fan1
++7 - fan2
++8 - +12VIN
++9 - -12VIN
++10 - -5VIN
++11 - fan3
++12 - chassis
++13 - temp3
++14 - VINR1
++15 - reserved
++16 - tart1
++17 - tart2
++18 - tart3
++19 - VSB
++20 - VBAT
++21 - fan4
++22 - fan5
++23 - reserved
++
++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.
++
++The driver only reads the chip values each 3 seconds; reading them more
++often will do no harm, but will return 'old' values.
++
++W83791D TODO:
++---------------
++Provide a patch for per-file alarms as discussed on the mailing list
++Provide a patch for smart-fan control (still need appropriate motherboard/fans)
+--- gregkh-2.6.orig/drivers/hwmon/Kconfig
++++ gregkh-2.6/drivers/hwmon/Kconfig
+@@ -406,6 +406,16 @@ config SENSORS_W83781D
+ This driver can also be built as a module. If so, the module
+ will be called w83781d.
+
++config SENSORS_W83791D
++ tristate "Winbond W83791D"
++ depends on HWMON && I2C && EXPERIMENTAL
++ select HWMON_VID
++ help
++ If you say yes here you get support for the Winbond W83791D chip.
++
++ This driver can also be built as a module. If so, the module
++ will be called w83791d.
++
+ config SENSORS_W83792D
+ tristate "Winbond W83792D"
+ depends on HWMON && I2C && EXPERIMENTAL
+--- gregkh-2.6.orig/drivers/hwmon/Makefile
++++ gregkh-2.6/drivers/hwmon/Makefile
+@@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o
+ obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
+ obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
+ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
++obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+
+ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
+ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
+--- /dev/null
++++ gregkh-2.6/drivers/hwmon/w83791d.c
+@@ -0,0 +1,1255 @@
++/*
++ w83791d.c - Part of lm_sensors, Linux kernel modules for hardware
++ monitoring
++
++ Copyright (C) 2006 Charles Spirakis <bezaur@gmail.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
++ w83791d 10 5 3 3 0x71 0x5ca3 yes no
++
++ The w83791d chip appears to be part way between the 83781d and the
++ 83792d. Thus, this file is derived from both the w83792d.c and
++ w83781d.c files, but its output is more along the lines of the
++ 83781d (which means there are no changes to the user-mode sensors
++ program which treats the 83791d as an 83781d).
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-vid.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/err.h>
++#include <linux/mutex.h>
++
++#define NUMBER_OF_VIN 10
++#define NUMBER_OF_FANIN 5
++#define NUMBER_OF_TEMPIN 3
++
++/* Addresses to scan */
++static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
++
++/* Insmod parameters */
++I2C_CLIENT_INSMOD_1(w83791d);
++I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
++ "{bus, clientaddr, subclientaddr1, subclientaddr2}");
++
++static int reset;
++module_param(reset, bool, 0);
++MODULE_PARM_DESC(reset, "Set to one to force a hardware chip reset");
++
++static int init;
++module_param(init, bool, 0);
++MODULE_PARM_DESC(init, "Set to one to force extra software initialization");
++
++/* The W83791D registers */
++static const u8 W83791D_REG_IN[NUMBER_OF_VIN] = {
++ 0x20, /* VCOREA in DataSheet */
++ 0x21, /* VINR0 in DataSheet */
++ 0x22, /* +3.3VIN in DataSheet */
++ 0x23, /* VDD5V in DataSheet */
++ 0x24, /* +12VIN in DataSheet */
++ 0x25, /* -12VIN in DataSheet */
++ 0x26, /* -5VIN in DataSheet */
++ 0xB0, /* 5VSB in DataSheet */
++ 0xB1, /* VBAT in DataSheet */
++ 0xB2 /* VINR1 in DataSheet */
++};
++
++static const u8 W83791D_REG_IN_MAX[NUMBER_OF_VIN] = {
++ 0x2B, /* VCOREA High Limit in DataSheet */
++ 0x2D, /* VINR0 High Limit in DataSheet */
++ 0x2F, /* +3.3VIN High Limit in DataSheet */
++ 0x31, /* VDD5V High Limit in DataSheet */
++ 0x33, /* +12VIN High Limit in DataSheet */
++ 0x35, /* -12VIN High Limit in DataSheet */
++ 0x37, /* -5VIN High Limit in DataSheet */
++ 0xB4, /* 5VSB High Limit in DataSheet */
++ 0xB6, /* VBAT High Limit in DataSheet */
++ 0xB8 /* VINR1 High Limit in DataSheet */
++};
++static const u8 W83791D_REG_IN_MIN[NUMBER_OF_VIN] = {
++ 0x2C, /* VCOREA Low Limit in DataSheet */
++ 0x2E, /* VINR0 Low Limit in DataSheet */
++ 0x30, /* +3.3VIN Low Limit in DataSheet */
++ 0x32, /* VDD5V Low Limit in DataSheet */
++ 0x34, /* +12VIN Low Limit in DataSheet */
++ 0x36, /* -12VIN Low Limit in DataSheet */
++ 0x38, /* -5VIN Low Limit in DataSheet */
++ 0xB5, /* 5VSB Low Limit in DataSheet */
++ 0xB7, /* VBAT Low Limit in DataSheet */
++ 0xB9 /* VINR1 Low Limit in DataSheet */
++};
++static const u8 W83791D_REG_FAN[NUMBER_OF_FANIN] = {
++ 0x28, /* FAN 1 Count in DataSheet */
++ 0x29, /* FAN 2 Count in DataSheet */
++ 0x2A, /* FAN 3 Count in DataSheet */
++ 0xBA, /* FAN 4 Count in DataSheet */
++ 0xBB, /* FAN 5 Count in DataSheet */
++};
++static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
++ 0x3B, /* FAN 1 Count Low Limit in DataSheet */
++ 0x3C, /* FAN 2 Count Low Limit in DataSheet */
++ 0x3D, /* FAN 3 Count Low Limit in DataSheet */
++ 0xBC, /* FAN 4 Count Low Limit in DataSheet */
++ 0xBD, /* FAN 5 Count Low Limit in DataSheet */
++};
++
++static const u8 W83791D_REG_FAN_CFG[2] = {
++ 0x84, /* FAN 1/2 configuration */
++ 0x95, /* FAN 3 configuration */
++};
++
++static const u8 W83791D_REG_FAN_DIV[3] = {
++ 0x47, /* contains FAN1 and FAN2 Divisor */
++ 0x4b, /* contains FAN3 Divisor */
++ 0x5C, /* contains FAN4 and FAN5 Divisor */
++};
++
++#define W83791D_REG_BANK 0x4E
++#define W83791D_REG_TEMP2_CONFIG 0xC2
++#define W83791D_REG_TEMP3_CONFIG 0xCA
++
++static const u8 W83791D_REG_TEMP1[3] = {
++ 0x27, /* TEMP 1 in DataSheet */
++ 0x39, /* TEMP 1 Over in DataSheet */
++ 0x3A, /* TEMP 1 Hyst in DataSheet */
++};
++
++static const u8 W83791D_REG_TEMP_ADD[2][6] = {
++ {0xC0, /* TEMP 2 in DataSheet */
++ 0xC1, /* TEMP 2(0.5 deg) in DataSheet */
++ 0xC5, /* TEMP 2 Over High part in DataSheet */
++ 0xC6, /* TEMP 2 Over Low part in DataSheet */
++ 0xC3, /* TEMP 2 Thyst High part in DataSheet */
++ 0xC4}, /* TEMP 2 Thyst Low part in DataSheet */
++ {0xC8, /* TEMP 3 in DataSheet */
++ 0xC9, /* TEMP 3(0.5 deg) in DataSheet */
++ 0xCD, /* TEMP 3 Over High part in DataSheet */
++ 0xCE, /* TEMP 3 Over Low part in DataSheet */
++ 0xCB, /* TEMP 3 Thyst High part in DataSheet */
++ 0xCC} /* TEMP 3 Thyst Low part in DataSheet */
++};
++
++#define W83791D_REG_BEEP_CONFIG 0x4D
++
++static const u8 W83791D_REG_BEEP_CTRL[3] = {
++ 0x56, /* BEEP Control Register 1 */
++ 0x57, /* BEEP Control Register 2 */
++ 0xA3, /* BEEP Control Register 3 */
++};
++
++#define W83791D_REG_CONFIG 0x40
++#define W83791D_REG_VID_FANDIV 0x47
++#define W83791D_REG_DID_VID4 0x49
++#define W83791D_REG_WCHIPID 0x58
++#define W83791D_REG_CHIPMAN 0x4F
++#define W83791D_REG_PIN 0x4B
++#define W83791D_REG_I2C_SUBADDR 0x4A
++
++#define W83791D_REG_ALARM1 0xA9 /* realtime status register1 */
++#define W83791D_REG_ALARM2 0xAA /* realtime status register2 */
++#define W83791D_REG_ALARM3 0xAB /* realtime status register3 */
++
++#define W83791D_REG_VBAT 0x5D
++#define W83791D_REG_I2C_ADDR 0x48
++
++/* The SMBus locks itself. The Winbond W83791D has a bank select register
++ (index 0x4e), but the driver only accesses registers in bank 0. Since
++ we don't switch banks, we don't need any special code to handle
++ locking access between bank switches */
++static inline int w83791d_read(struct i2c_client *client, u8 reg)
++{
++ return i2c_smbus_read_byte_data(client, reg);
++}
++
++static inline int w83791d_write(struct i2c_client *client, u8 reg, u8 value)
++{
++ return i2c_smbus_write_byte_data(client, reg, value);
++}
++
++/* The analog voltage inputs have 16mV LSB. Since the sysfs output is
++ in mV as would be measured on the chip input pin, need to just
++ multiply/divide by 16 to translate from/to register values. */
++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255))
++#define IN_FROM_REG(val) ((val) * 16)
++
++static 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))))
++
++/* for temp1 which is 8-bit resolution, LSB = 1 degree Celsius */
++#define TEMP1_FROM_REG(val) ((val) * 1000)
++#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
++ (val) >= 127000 ? 127 : \
++ (val) < 0 ? ((val) - 500) / 1000 : \
++ ((val) + 500) / 1000)
++
++/* for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius
++ Assumes the top 8 bits are the integral amount and the bottom 8 bits
++ are the fractional amount. Since we only have 0.5 degree resolution,
++ the bottom 7 bits will always be zero */
++#define TEMP23_FROM_REG(val) ((val) / 128 * 500)
++#define TEMP23_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
++ (val) >= 127500 ? 0x7F80 : \
++ (val) < 0 ? ((val) - 250) / 500 * 128 : \
++ ((val) + 250) / 500 * 128)
++
++
++#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
++#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
++
++#define DIV_FROM_REG(val) (1 << (val))
++
++static u8 div_to_reg(int nr, long val)
++{
++ int i;
++ int max;
++
++ /* first three fan's divisor max out at 8, rest max out at 128 */
++ max = (nr < 3) ? 8 : 128;
++ val = SENSORS_LIMIT(val, 1, max) >> 1;
++ for (i = 0; i < 7; i++) {
++ if (val == 0)
++ break;
++ val >>= 1;
++ }
++ return (u8) i;
++}
++
++struct w83791d_data {
++ struct i2c_client client;
++ struct class_device *class_dev;
++ struct mutex update_lock;
++
++ char valid; /* !=0 if following fields are valid */
++ unsigned long last_updated; /* In jiffies */
++
++ /* array of 2 pointers to subclients */
++ struct i2c_client *lm75[2];
++
++ /* volts */
++ u8 in[NUMBER_OF_VIN]; /* Register value */
++ u8 in_max[NUMBER_OF_VIN]; /* Register value */
++ u8 in_min[NUMBER_OF_VIN]; /* Register value */
++
++ /* fans */
++ u8 fan[NUMBER_OF_FANIN]; /* Register value */
++ u8 fan_min[NUMBER_OF_FANIN]; /* Register value */
++ u8 fan_div[NUMBER_OF_FANIN]; /* Register encoding, shifted right */
++
++ /* Temperature sensors */
++
++ s8 temp1[3]; /* current, over, thyst */
++ s16 temp_add[2][3]; /* fixed point value. Top 8 bits are the
++ integral part, bottom 8 bits are the
++ fractional part. We only use the top
++ 9 bits as the resolution is only
++ to the 0.5 degree C...
++ two sensors with three values
++ (cur, over, hyst) */
++
++ /* Misc */
++ u32 alarms; /* realtime status register encoding,combined */
++ u8 beep_enable; /* Global beep enable */
++ u32 beep_mask; /* Mask off specific beeps */
++ u8 vid; /* Register encoding, combined */
++ u8 vrm; /* hwmon-vid */
++};
++
++static int w83791d_attach_adapter(struct i2c_adapter *adapter);
++static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind);
++static int w83791d_detach_client(struct i2c_client *client);
++
++static int w83791d_read(struct i2c_client *client, u8 register);
++static int w83791d_write(struct i2c_client *client, u8 register, u8 value);
++static struct w83791d_data *w83791d_update_device(struct device *dev);
++
++#ifdef DEBUG
++static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
++#endif
++
++static void w83791d_init_client(struct i2c_client *client);
++
++static struct i2c_driver w83791d_driver = {
++ .driver = {
++ .name = "w83791d",
++ },
++ .attach_adapter = w83791d_attach_adapter,
++ .detach_client = w83791d_detach_client,
++};
++
++/* following are the sysfs callback functions */
++#define show_in_reg(reg) \
++static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
++ char *buf) \
++{ \
++ struct sensor_device_attribute *sensor_attr = \
++ to_sensor_dev_attr(attr); \
++ struct w83791d_data *data = w83791d_update_device(dev); \
++ int nr = sensor_attr->index; \
++ return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[nr])); \
++}
++
++show_in_reg(in);
++show_in_reg(in_min);
++show_in_reg(in_max);
++
++#define store_in_reg(REG, reg) \
++static ssize_t store_in_##reg(struct device *dev, \
++ struct device_attribute *attr, \
++ const char *buf, size_t count) \
++{ \
++ struct sensor_device_attribute *sensor_attr = \
++ to_sensor_dev_attr(attr); \
++ struct i2c_client *client = to_i2c_client(dev); \
++ struct w83791d_data *data = i2c_get_clientdata(client); \
++ unsigned long val = simple_strtoul(buf, NULL, 10); \
++ int nr = sensor_attr->index; \
++ \
++ mutex_lock(&data->update_lock); \
++ data->in_##reg[nr] = IN_TO_REG(val); \
++ w83791d_write(client, W83791D_REG_IN_##REG[nr], data->in_##reg[nr]); \
++ mutex_unlock(&data->update_lock); \
++ \
++ return count; \
++}
++store_in_reg(MIN, min);
++store_in_reg(MAX, max);
++
++static struct sensor_device_attribute sda_in_input[] = {
++ SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
++ SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
++ SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
++ SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
++ SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
++ SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
++ SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
++ SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
++ SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
++ SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
++};
++
++static struct sensor_device_attribute sda_in_min[] = {
++ SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
++ SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
++ SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
++ SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
++ SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
++ SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
++ SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
++ SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
++ SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
++ SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
++};
++
++static struct sensor_device_attribute sda_in_max[] = {
++ SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
++ SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
++ SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
++ SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
++ SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
++ SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
++ SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
++ SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
++ SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
++ SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
++};
++
++#define show_fan_reg(reg) \
++static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
++ char *buf) \
++{ \
++ struct sensor_device_attribute *sensor_attr = \
++ to_sensor_dev_attr(attr); \
++ struct w83791d_data *data = w83791d_update_device(dev); \
++ int nr = sensor_attr->index; \
++ return sprintf(buf,"%d\n", \
++ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
++}
++
++show_fan_reg(fan);
++show_fan_reg(fan_min);
++
++static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ unsigned long val = simple_strtoul(buf, NULL, 10);
++ int nr = sensor_attr->index;
++
++ mutex_lock(&data->update_lock);
++ data->fan_min[nr] = fan_to_reg(val, DIV_FROM_REG(data->fan_div[nr]));
++ w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
++static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ int nr = sensor_attr->index;
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr]));
++}
++
++/* Note: we save and restore the fan minimum here, because its value is
++ determined in part by the fan divisor. This follows the principle of
++ least suprise; the user doesn't expect the fan minimum to change just
++ because the divisor changed. */
++static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ int nr = sensor_attr->index;
++ unsigned long min;
++ u8 tmp_fan_div;
++ u8 fan_div_reg;
++ int indx = 0;
++ u8 keep_mask = 0;
++ u8 new_shift = 0;
++
++ /* Save fan_min */
++ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
++
++ mutex_lock(&data->update_lock);
++ data->fan_div[nr] = div_to_reg(nr, simple_strtoul(buf, NULL, 10));
++
++ switch (nr) {
++ case 0:
++ indx = 0;
++ keep_mask = 0xcf;
++ new_shift = 4;
++ break;
++ case 1:
++ indx = 0;
++ keep_mask = 0x3f;
++ new_shift = 6;
++ break;
++ case 2:
++ indx = 1;
++ keep_mask = 0x3f;
++ new_shift = 6;
++ break;
++ case 3:
++ indx = 2;
++ keep_mask = 0xf8;
++ new_shift = 0;
++ break;
++ case 4:
++ indx = 2;
++ keep_mask = 0x8f;
++ new_shift = 4;
++ break;
++#ifdef DEBUG
++ default:
++ dev_warn(dev, "store_fan_div: Unexpected nr seen: %d\n", nr);
++ count = -EINVAL;
++ goto err_exit;
++#endif
++ }
++
++ fan_div_reg = w83791d_read(client, W83791D_REG_FAN_DIV[indx])
++ & keep_mask;
++ tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
++
++ w83791d_write(client, W83791D_REG_FAN_DIV[indx],
++ fan_div_reg | tmp_fan_div);
++
++ /* Restore fan_min */
++ data->fan_min[nr] = fan_to_reg(min, DIV_FROM_REG(data->fan_div[nr]));
++ w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
++
++#ifdef DEBUG
++err_exit:
++#endif
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
++static struct sensor_device_attribute sda_fan_input[] = {
++ SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
++ SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
++ SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
++ SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
++ SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
++};
++
++static struct sensor_device_attribute sda_fan_min[] = {
++ SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO,
++ show_fan_min, store_fan_min, 0),
++ SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO,
++ show_fan_min, store_fan_min, 1),
++ SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO,
++ show_fan_min, store_fan_min, 2),
++ SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO,
++ show_fan_min, store_fan_min, 3),
++ SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO,
++ show_fan_min, store_fan_min, 4),
++};
++
++static struct sensor_device_attribute sda_fan_div[] = {
++ SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO,
++ show_fan_div, store_fan_div, 0),
++ SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO,
++ show_fan_div, store_fan_div, 1),
++ SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO,
++ show_fan_div, store_fan_div, 2),
++ SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO,
++ show_fan_div, store_fan_div, 3),
++ SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO,
++ show_fan_div, store_fan_div, 4),
++};
++
++/* read/write the temperature1, includes measured value and limits */
++static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
++ char *buf)
++{
++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp1[attr->index]));
++}
++
++static ssize_t store_temp1(struct device *dev, struct device_attribute *devattr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ long val = simple_strtol(buf, NULL, 10);
++ int nr = attr->index;
++
++ mutex_lock(&data->update_lock);
++ data->temp1[nr] = TEMP1_TO_REG(val);
++ w83791d_write(client, W83791D_REG_TEMP1[nr], data->temp1[nr]);
++ mutex_unlock(&data->update_lock);
++ return count;
++}
++
++/* read/write temperature2-3, includes measured value and limits */
++static ssize_t show_temp23(struct device *dev, struct device_attribute *devattr,
++ char *buf)
++{
++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
++ struct w83791d_data *data = w83791d_update_device(dev);
++ int nr = attr->nr;
++ int index = attr->index;
++ return sprintf(buf, "%d\n", TEMP23_FROM_REG(data->temp_add[nr][index]));
++}
++
++static ssize_t store_temp23(struct device *dev,
++ struct device_attribute *devattr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ long val = simple_strtol(buf, NULL, 10);
++ int nr = attr->nr;
++ int index = attr->index;
++
++ mutex_lock(&data->update_lock);
++ data->temp_add[nr][index] = TEMP23_TO_REG(val);
++ w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2],
++ data->temp_add[nr][index] >> 8);
++ w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2 + 1],
++ data->temp_add[nr][index] & 0x80);
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
++static struct sensor_device_attribute_2 sda_temp_input[] = {
++ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0),
++ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0),
++ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0),
++};
++
++static struct sensor_device_attribute_2 sda_temp_max[] = {
++ SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
++ show_temp1, store_temp1, 0, 1),
++ SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
++ show_temp23, store_temp23, 0, 1),
++ SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR,
++ show_temp23, store_temp23, 1, 1),
++};
++
++static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
++ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
++ show_temp1, store_temp1, 0, 2),
++ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
++ show_temp23, store_temp23, 0, 2),
++ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
++ show_temp23, store_temp23, 1, 2),
++};
++
++
++/* get reatime status of all sensors items: voltage, temp, fan */
++static ssize_t show_alarms_reg(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%u\n", data->alarms);
++}
++
++static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
++
++/* Beep control */
++
++#define GLOBAL_BEEP_ENABLE_SHIFT 15
++#define GLOBAL_BEEP_ENABLE_MASK (1 << GLOBAL_BEEP_ENABLE_SHIFT)
++
++static ssize_t show_beep_enable(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%d\n", data->beep_enable);
++}
++
++static ssize_t show_beep_mask(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%d\n", BEEP_MASK_FROM_REG(data->beep_mask));
++}
++
++
++static ssize_t store_beep_mask(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ long val = simple_strtol(buf, NULL, 10);
++ int i;
++
++ mutex_lock(&data->update_lock);
++
++ /* The beep_enable state overrides any enabling request from
++ the masks */
++ data->beep_mask = BEEP_MASK_TO_REG(val) & ~GLOBAL_BEEP_ENABLE_MASK;
++ data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
++
++ val = data->beep_mask;
++
++ for (i = 0; i < 3; i++) {
++ w83791d_write(client, W83791D_REG_BEEP_CTRL[i], (val & 0xff));
++ val >>= 8;
++ }
++
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
++static ssize_t store_beep_enable(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ long val = simple_strtol(buf, NULL, 10);
++
++ mutex_lock(&data->update_lock);
++
++ data->beep_enable = val ? 1 : 0;
++
++ /* Keep the full mask value in sync with the current enable */
++ data->beep_mask &= ~GLOBAL_BEEP_ENABLE_MASK;
++ data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
++
++ /* The global control is in the second beep control register
++ so only need to update that register */
++ val = (data->beep_mask >> 8) & 0xff;
++
++ w83791d_write(client, W83791D_REG_BEEP_CTRL[1], val);
++
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
++static struct sensor_device_attribute sda_beep_ctrl[] = {
++ SENSOR_ATTR(beep_enable, S_IRUGO | S_IWUSR,
++ show_beep_enable, store_beep_enable, 0),
++ SENSOR_ATTR(beep_mask, S_IRUGO | S_IWUSR,
++ show_beep_mask, store_beep_mask, 1)
++};
++
++/* cpu voltage regulation information */
++static ssize_t show_vid_reg(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
++}
++
++static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
++
++static ssize_t show_vrm_reg(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct w83791d_data *data = w83791d_update_device(dev);
++ return sprintf(buf, "%d\n", data->vrm);
++}
++
++static ssize_t store_vrm_reg(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ unsigned long val = simple_strtoul(buf, NULL, 10);
++
++ /* No lock needed as vrm is internal to the driver
++ (not read from a chip register) and so is not
++ updated in w83791d_update_device() */
++ data->vrm = val;
++
++ return count;
++}
++
++static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
++
++/* This function is called when:
++ * w83791d_driver is inserted (when this module is loaded), for each
++ available adapter
++ * when a new adapter is inserted (and w83791d_driver is still present) */
++static int w83791d_attach_adapter(struct i2c_adapter *adapter)
++{
++ if (!(adapter->class & I2C_CLASS_HWMON))
++ return 0;
++ return i2c_probe(adapter, &addr_data, w83791d_detect);
++}
++
++
++static int w83791d_create_subclient(struct i2c_adapter *adapter,
++ struct i2c_client *client, int addr,
++ struct i2c_client **sub_cli)
++{
++ int err;
++ struct i2c_client *sub_client;
++
++ (*sub_cli) = sub_client =
++ kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (!(sub_client)) {
++ return -ENOMEM;
++ }
++ sub_client->addr = 0x48 + addr;
++ i2c_set_clientdata(sub_client, NULL);
++ sub_client->adapter = adapter;
++ sub_client->driver = &w83791d_driver;
++ strlcpy(sub_client->name, "w83791d subclient", I2C_NAME_SIZE);
++ if ((err = i2c_attach_client(sub_client))) {
++ dev_err(&client->dev, "subclient registration "
++ "at address 0x%x failed\n", sub_client->addr);
++ kfree(sub_client);
++ return err;
++ }
++ return 0;
++}
++
++
++static int w83791d_detect_subclients(struct i2c_adapter *adapter, int address,
++ int kind, struct i2c_client *client)
++{
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ int i, id, err;
++ u8 val;
++
++ id = i2c_adapter_id(adapter);
++ if (force_subclients[0] == id && force_subclients[1] == address) {
++ for (i = 2; i <= 3; i++) {
++ if (force_subclients[i] < 0x48 ||
++ force_subclients[i] > 0x4f) {
++ dev_err(&client->dev,
++ "invalid subclient "
++ "address %d; must be 0x48-0x4f\n",
++ force_subclients[i]);
++ err = -ENODEV;
++ goto error_sc_0;
++ }
++ }
++ w83791d_write(client, W83791D_REG_I2C_SUBADDR,
++ (force_subclients[2] & 0x07) |
++ ((force_subclients[3] & 0x07) << 4));
++ }
++
++ val = w83791d_read(client, W83791D_REG_I2C_SUBADDR);
++ if (!(val & 0x08)) {
++ err = w83791d_create_subclient(adapter, client,
++ val & 0x7, &data->lm75[0]);
++ if (err < 0)
++ goto error_sc_0;
++ }
++ if (!(val & 0x80)) {
++ if ((data->lm75[0] != NULL) &&
++ ((val & 0x7) == ((val >> 4) & 0x7))) {
++ dev_err(&client->dev,
++ "duplicate addresses 0x%x, "
++ "use force_subclient\n",
++ data->lm75[0]->addr);
++ err = -ENODEV;
++ goto error_sc_1;
++ }
++ err = w83791d_create_subclient(adapter, client,
++ (val >> 4) & 0x7, &data->lm75[1]);
++ if (err < 0)
++ goto error_sc_1;
++ }
++
++ return 0;
++
++/* Undo inits in case of errors */
++
++error_sc_1:
++ if (data->lm75[0] != NULL) {
++ i2c_detach_client(data->lm75[0]);
++ kfree(data->lm75[0]);
++ }
++error_sc_0:
++ return err;
++}
++
++
++static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *client;
++ struct device *dev;
++ struct w83791d_data *data;
++ int i, val1, val2;
++ int err = 0;
++ 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 w83791d_{read,write}_value. */
++ if (!(data = kzalloc(sizeof(struct w83791d_data), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto error0;
++ }
++
++ client = &data->client;
++ dev = &client->dev;
++ i2c_set_clientdata(client, data);
++ client->addr = address;
++ client->adapter = adapter;
++ client->driver = &w83791d_driver;
++ mutex_init(&data->update_lock);
++
++ /* Now, we do the remaining detection. */
++
++ /* The w83791d may be stuck in some other bank than bank 0. This may
++ make reading other information impossible. Specify a force=...
++ parameter, and the Winbond will be reset to the right bank. */
++ if (kind < 0) {
++ if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) {
++ dev_dbg(dev, "Detection failed at step 1\n");
++ goto error1;
++ }
++ val1 = w83791d_read(client, W83791D_REG_BANK);
++ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
++ /* Check for Winbond ID if in bank 0 */
++ if (!(val1 & 0x07)) {
++ /* yes it is Bank0 */
++ if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
++ ((val1 & 0x80) && (val2 != 0x5c))) {
++ dev_dbg(dev, "Detection failed at step 2\n");
++ goto error1;
++ }
++ }
++ /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
++ should match */
++ if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) {
++ dev_dbg(dev, "Detection failed at step 3\n");
++ goto error1;
++ }
++ }
++
++ /* We either have a force parameter or we have reason to
++ believe it is a Winbond chip. Either way, we want bank 0 and
++ Vendor ID high byte */
++ val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78;
++ w83791d_write(client, W83791D_REG_BANK, val1 | 0x80);
++
++ /* Verify it is a Winbond w83791d */
++ if (kind <= 0) {
++ /* get vendor ID */
++ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
++ if (val2 != 0x5c) { /* the vendor is NOT Winbond */
++ dev_dbg(dev, "Detection failed at step 4\n");
++ goto error1;
++ }
++ val1 = w83791d_read(client, W83791D_REG_WCHIPID);
++ if (val1 == 0x71) {
++ kind = w83791d;
++ } else {
++ if (kind == 0)
++ dev_warn(dev,
++ "w83791d: Ignoring 'force' parameter "
++ "for unknown chip at adapter %d, "
++ "address 0x%02x\n",
++ i2c_adapter_id(adapter), address);
++ goto error1;
++ }
++ }
++
++ if (kind == w83791d) {
++ client_name = "w83791d";
++ } else {
++ dev_err(dev, "w83791d: Internal error: unknown kind (%d)?!?",
++ kind);
++ goto error1;
++ }
++
++#ifdef DEBUG
++ val1 = w83791d_read(client, W83791D_REG_DID_VID4);
++ dev_dbg(dev, "Device ID version: %d.%d (0x%02x)\n",
++ (val1 >> 5) & 0x07, (val1 >> 1) & 0x0f, val1);
++#endif
++
++ /* Fill in the remaining client fields and put into the global list */
++ strlcpy(client->name, client_name, I2C_NAME_SIZE);
++
++ /* Tell the I2C layer a new client has arrived */
++ if ((err = i2c_attach_client(client)))
++ goto error1;
++
++ if ((err = w83791d_detect_subclients(adapter, address, kind, client)))
++ goto error2;
++
++ /* Initialize the chip */
++ w83791d_init_client(client);
++
++ /* If the fan_div is changed, make sure there is a rational
++ fan_min in place */
++ for (i = 0; i < NUMBER_OF_FANIN; i++) {
++ data->fan_min[i] = w83791d_read(client, W83791D_REG_FAN_MIN[i]);
++ }
++
++ /* Register sysfs hooks */
++ data->class_dev = hwmon_device_register(dev);
++ if (IS_ERR(data->class_dev)) {
++ err = PTR_ERR(data->class_dev);
++ goto error3;
++ }
++
++ for (i = 0; i < NUMBER_OF_VIN; i++) {
++ device_create_file(dev, &sda_in_input[i].dev_attr);
++ device_create_file(dev, &sda_in_min[i].dev_attr);
++ device_create_file(dev, &sda_in_max[i].dev_attr);
++ }
++
++ for (i = 0; i < NUMBER_OF_FANIN; i++) {
++ device_create_file(dev, &sda_fan_input[i].dev_attr);
++ device_create_file(dev, &sda_fan_div[i].dev_attr);
++ device_create_file(dev, &sda_fan_min[i].dev_attr);
++ }
++
++ for (i = 0; i < NUMBER_OF_TEMPIN; i++) {
++ device_create_file(dev, &sda_temp_input[i].dev_attr);
++ device_create_file(dev, &sda_temp_max[i].dev_attr);
++ device_create_file(dev, &sda_temp_max_hyst[i].dev_attr);
++ }
++
++ device_create_file(dev, &dev_attr_alarms);
++
++ for (i = 0; i < ARRAY_SIZE(sda_beep_ctrl); i++) {
++ device_create_file(dev, &sda_beep_ctrl[i].dev_attr);
++ }
++
++ device_create_file(dev, &dev_attr_cpu0_vid);
++ device_create_file(dev, &dev_attr_vrm);
++
++ return 0;
++
++error3:
++ if (data->lm75[0] != NULL) {
++ i2c_detach_client(data->lm75[0]);
++ kfree(data->lm75[0]);
++ }
++ if (data->lm75[1] != NULL) {
++ i2c_detach_client(data->lm75[1]);
++ kfree(data->lm75[1]);
++ }
++error2:
++ i2c_detach_client(client);
++error1:
++ kfree(data);
++error0:
++ return err;
++}
++
++static int w83791d_detach_client(struct i2c_client *client)
++{
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ int err;
++
++ /* main client */
++ if (data)
++ hwmon_device_unregister(data->class_dev);
++
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ /* main client */
++ if (data)
++ kfree(data);
++ /* subclient */
++ else
++ kfree(client);
++
++ return 0;
++}
++
++static void w83791d_init_client(struct i2c_client *client)
++{
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ u8 tmp;
++ u8 old_beep;
++
++ /* The difference between reset and init is that reset
++ does a hard reset of the chip via index 0x40, bit 7,
++ but init simply forces certain registers to have "sane"
++ values. The hope is that the BIOS has done the right
++ thing (which is why the default is reset=0, init=0),
++ but if not, reset is the hard hammer and init
++ is the soft mallet both of which are trying to whack
++ things into place...
++ NOTE: The data sheet makes a distinction between
++ "power on defaults" and "reset by MR". As far as I can tell,
++ the hard reset puts everything into a power-on state so I'm
++ not sure what "reset by MR" means or how it can happen.
++ */
++ if (reset || init) {
++ /* keep some BIOS settings when we... */
++ old_beep = w83791d_read(client, W83791D_REG_BEEP_CONFIG);
++
++ if (reset) {
++ /* ... reset the chip and ... */
++ w83791d_write(client, W83791D_REG_CONFIG, 0x80);
++ }
++
++ /* ... disable power-on abnormal beep */
++ w83791d_write(client, W83791D_REG_BEEP_CONFIG, old_beep | 0x80);
++
++ /* disable the global beep (not done by hard reset) */
++ tmp = w83791d_read(client, W83791D_REG_BEEP_CTRL[1]);
++ w83791d_write(client, W83791D_REG_BEEP_CTRL[1], tmp & 0xef);
++
++ if (init) {
++ /* Make sure monitoring is turned on for add-ons */
++ tmp = w83791d_read(client, W83791D_REG_TEMP2_CONFIG);
++ if (tmp & 1) {
++ w83791d_write(client, W83791D_REG_TEMP2_CONFIG,
++ tmp & 0xfe);
++ }
++
++ tmp = w83791d_read(client, W83791D_REG_TEMP3_CONFIG);
++ if (tmp & 1) {
++ w83791d_write(client, W83791D_REG_TEMP3_CONFIG,
++ tmp & 0xfe);
++ }
++
++ /* Start monitoring */
++ tmp = w83791d_read(client, W83791D_REG_CONFIG) & 0xf7;
++ w83791d_write(client, W83791D_REG_CONFIG, tmp | 0x01);
++ }
++ }
++
++ data->vrm = vid_which_vrm();
++}
++
++static struct w83791d_data *w83791d_update_device(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct w83791d_data *data = i2c_get_clientdata(client);
++ int i, j;
++ u8 reg_array_tmp[3];
++
++ mutex_lock(&data->update_lock);
++
++ if (time_after(jiffies, data->last_updated + (HZ * 3))
++ || !data->valid) {
++ dev_dbg(dev, "Starting w83791d device update\n");
++
++ /* Update the voltages measured value and limits */
++ for (i = 0; i < NUMBER_OF_VIN; i++) {
++ data->in[i] = w83791d_read(client,
++ W83791D_REG_IN[i]);
++ data->in_max[i] = w83791d_read(client,
++ W83791D_REG_IN_MAX[i]);
++ data->in_min[i] = w83791d_read(client,
++ W83791D_REG_IN_MIN[i]);
++ }
++
++ /* Update the fan counts and limits */
++ for (i = 0; i < NUMBER_OF_FANIN; i++) {
++ /* Update the Fan measured value and limits */
++ data->fan[i] = w83791d_read(client,
++ W83791D_REG_FAN[i]);
++ data->fan_min[i] = w83791d_read(client,
++ W83791D_REG_FAN_MIN[i]);
++ }
++
++ /* Update the fan divisor */
++ for (i = 0; i < 3; i++) {
++ reg_array_tmp[i] = w83791d_read(client,
++ W83791D_REG_FAN_DIV[i]);
++ }
++ data->fan_div[0] = (reg_array_tmp[0] >> 4) & 0x03;
++ data->fan_div[1] = (reg_array_tmp[0] >> 6) & 0x03;
++ data->fan_div[2] = (reg_array_tmp[1] >> 6) & 0x03;
++ data->fan_div[3] = reg_array_tmp[2] & 0x07;
++ data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07;
++
++ /* Update the first temperature sensor */
++ for (i = 0; i < 3; i++) {
++ data->temp1[i] = w83791d_read(client,
++ W83791D_REG_TEMP1[i]);
++ }
++
++ /* Update the rest of the temperature sensors */
++ for (i = 0; i < 2; i++) {
++ for (j = 0; j < 3; j++) {
++ data->temp_add[i][j] =
++ (w83791d_read(client,
++ W83791D_REG_TEMP_ADD[i][j * 2]) << 8) |
++ w83791d_read(client,
++ W83791D_REG_TEMP_ADD[i][j * 2 + 1]);
++ }
++ }
++
++ /* Update the realtime status */
++ data->alarms =
++ w83791d_read(client, W83791D_REG_ALARM1) +
++ (w83791d_read(client, W83791D_REG_ALARM2) << 8) +
++ (w83791d_read(client, W83791D_REG_ALARM3) << 16);
++
++ /* Update the beep configuration information */
++ data->beep_mask =
++ w83791d_read(client, W83791D_REG_BEEP_CTRL[0]) +
++ (w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) +
++ (w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16);
++
++ data->beep_enable =
++ (data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01;
++
++ /* Update the cpu voltage information */
++ i = w83791d_read(client, W83791D_REG_VID_FANDIV);
++ data->vid = i & 0x0f;
++ data->vid |= (w83791d_read(client, W83791D_REG_DID_VID4) & 0x01)
++ << 4;
++
++ data->last_updated = jiffies;
++ data->valid = 1;
++ }
++
++ mutex_unlock(&data->update_lock);
++
++#ifdef DEBUG
++ w83791d_print_debug(data, dev);
++#endif
++
++ return data;
++}
++
++#ifdef DEBUG
++static void w83791d_print_debug(struct w83791d_data *data, struct device *dev)
++{
++ int i = 0, j = 0;
++
++ dev_dbg(dev, "======Start of w83791d debug values======\n");
++ dev_dbg(dev, "%d set of Voltages: ===>\n", NUMBER_OF_VIN);
++ for (i = 0; i < NUMBER_OF_VIN; i++) {
++ dev_dbg(dev, "vin[%d] is: 0x%02x\n", i, data->in[i]);
++ dev_dbg(dev, "vin[%d] min is: 0x%02x\n", i, data->in_min[i]);
++ dev_dbg(dev, "vin[%d] max is: 0x%02x\n", i, data->in_max[i]);
++ }
++ dev_dbg(dev, "%d set of Fan Counts/Divisors: ===>\n", NUMBER_OF_FANIN);
++ for (i = 0; i < NUMBER_OF_FANIN; i++) {
++ dev_dbg(dev, "fan[%d] is: 0x%02x\n", i, data->fan[i]);
++ dev_dbg(dev, "fan[%d] min is: 0x%02x\n", i, data->fan_min[i]);
++ dev_dbg(dev, "fan_div[%d] is: 0x%02x\n", i, data->fan_div[i]);
++ }
++
++ /* temperature math is signed, but only print out the
++ bits that matter */
++ dev_dbg(dev, "%d set of Temperatures: ===>\n", NUMBER_OF_TEMPIN);
++ for (i = 0; i < 3; i++) {
++ dev_dbg(dev, "temp1[%d] is: 0x%02x\n", i, (u8) data->temp1[i]);
++ }
++ for (i = 0; i < 2; i++) {
++ for (j = 0; j < 3; j++) {
++ dev_dbg(dev, "temp_add[%d][%d] is: 0x%04x\n", i, j,
++ (u16) data->temp_add[i][j]);
++ }
++ }
++
++ dev_dbg(dev, "Misc Information: ===>\n");
++ dev_dbg(dev, "alarm is: 0x%08x\n", data->alarms);
++ dev_dbg(dev, "beep_mask is: 0x%08x\n", data->beep_mask);
++ dev_dbg(dev, "beep_enable is: %d\n", data->beep_enable);
++ dev_dbg(dev, "vid is: 0x%02x\n", data->vid);
++ dev_dbg(dev, "vrm is: 0x%02x\n", data->vrm);
++ dev_dbg(dev, "=======End of w83791d debug values========\n");
++ dev_dbg(dev, "\n");
++}
++#endif
++
++static int __init sensors_w83791d_init(void)
++{
++ return i2c_add_driver(&w83791d_driver);
++}
++
++static void __exit sensors_w83791d_exit(void)
++{
++ i2c_del_driver(&w83791d_driver);
++}
++
++MODULE_AUTHOR("Charles Spirakis <bezaur@gmail.com>");
++MODULE_DESCRIPTION("W83791D driver");
++MODULE_LICENSE("GPL");
++
++module_init(sensors_w83791d_init);
++module_exit(sensors_w83791d_exit);
diff --git a/i2c/i2c-nforce2-add-mcp51-mcp55-support.patch b/i2c/i2c-nforce2-add-mcp51-mcp55-support.patch
new file mode 100644
index 0000000000000..8865ac32e1095
--- /dev/null
+++ b/i2c/i2c-nforce2-add-mcp51-mcp55-support.patch
@@ -0,0 +1,131 @@
+From khali@linux-fr.org Tue Apr 25 05:18:16 2006
+Date: Tue, 25 Apr 2006 14:18:16 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Subject: I2C: i2c-nforce2: Add support for the nForce4 MCP51 and MCP55
+Message-Id: <20060425141816.a6dcb410.khali@linux-fr.org>
+Content-Disposition: inline; filename=i2c-nforce2-add-mcp51-mcp55-support.patch
+
+Add support for the new nForce4 MCP51 (also known as nForce 410 or
+430) and nForce4 MCP55 to the i2c-nforce2 driver. Some code changes
+were required because the base I/O address registers have changed in
+these versions. Standard BARs are now being used, while the original
+nForce2 chips used non-standard ones.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/i2c/busses/i2c-nforce2 | 2 +
+ drivers/i2c/busses/i2c-nforce2.c | 38 ++++++++++++++++++++++++-----------
+ include/linux/pci_ids.h | 2 +
+ 3 files changed, 31 insertions(+), 11 deletions(-)
+
+--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-nforce2
++++ gregkh-2.6/Documentation/i2c/busses/i2c-nforce2
+@@ -7,6 +7,8 @@ Supported adapters:
+ * nForce3 250Gb MCP 10de:00E4
+ * nForce4 MCP 10de:0052
+ * nForce4 MCP-04 10de:0034
++ * nForce4 MCP51 10de:0264
++ * nForce4 MCP55 10de:0368
+
+ Datasheet: not publically available, but seems to be similar to the
+ AMD-8111 SMBus 2.0 adapter.
+--- gregkh-2.6.orig/drivers/i2c/busses/i2c-nforce2.c
++++ gregkh-2.6/drivers/i2c/busses/i2c-nforce2.c
+@@ -31,6 +31,8 @@
+ nForce3 250Gb MCP 00E4
+ nForce4 MCP 0052
+ nForce4 MCP-04 0034
++ nForce4 MCP51 0264
++ nForce4 MCP55 0368
+
+ This driver supports the 2 SMBuses that are included in the MCP of the
+ nForce2/3/4 chipsets.
+@@ -64,6 +66,7 @@ struct nforce2_smbus {
+
+ /*
+ * nVidia nForce2 SMBus control register definitions
++ * (Newer incarnations use standard BARs 4 and 5 instead)
+ */
+ #define NFORCE_PCI_SMB1 0x50
+ #define NFORCE_PCI_SMB2 0x54
+@@ -259,6 +262,8 @@ static struct pci_device_id nforce2_ids[
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) },
++ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) },
++ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) },
+ { 0 }
+ };
+
+@@ -266,19 +271,29 @@ static struct pci_device_id nforce2_ids[
+ MODULE_DEVICE_TABLE (pci, nforce2_ids);
+
+
+-static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg,
+- struct nforce2_smbus *smbus, char *name)
++static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
++ int alt_reg, struct nforce2_smbus *smbus, const char *name)
+ {
+- u16 iobase;
+ int error;
+
+- if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) {
+- dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name);
+- return -1;
++ smbus->base = pci_resource_start(dev, bar);
++ if (smbus->base) {
++ smbus->size = pci_resource_len(dev, bar);
++ } else {
++ /* Older incarnations of the device used non-standard BARs */
++ u16 iobase;
++
++ if (pci_read_config_word(dev, alt_reg, &iobase)
++ != PCIBIOS_SUCCESSFUL) {
++ dev_err(&dev->dev, "Error reading PCI config for %s\n",
++ name);
++ return -1;
++ }
++
++ smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
++ smbus->size = 8;
+ }
+- smbus->dev = dev;
+- smbus->base = iobase & 0xfffc;
+- smbus->size = 8;
++ smbus->dev = dev;
+
+ if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
+ dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n",
+@@ -313,12 +328,13 @@ static int __devinit nforce2_probe(struc
+ pci_set_drvdata(dev, smbuses);
+
+ /* SMBus adapter 1 */
+- res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
++ res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
+ if (res1 < 0) {
+ dev_err(&dev->dev, "Error probing SMB1.\n");
+ smbuses[0].base = 0; /* to have a check value */
+ }
+- res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
++ /* SMBus adapter 2 */
++ res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
+ if (res2 < 0) {
+ dev_err(&dev->dev, "Error probing SMB2.\n");
+ smbuses[1].base = 0; /* to have a check value */
+--- gregkh-2.6.orig/include/linux/pci_ids.h
++++ gregkh-2.6/include/linux/pci_ids.h
+@@ -1129,9 +1129,11 @@
+ #define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258
+ #define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259
+ #define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B
++#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267
++#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F
diff --git a/i2c/i2c-piix4-fix-typo-in-documentation.patch b/i2c/i2c-piix4-fix-typo-in-documentation.patch
new file mode 100644
index 0000000000000..9952790c585a9
--- /dev/null
+++ b/i2c/i2c-piix4-fix-typo-in-documentation.patch
@@ -0,0 +1,28 @@
+From khali@linux-fr.org Tue Apr 25 04:29:22 2006
+Date: Tue, 25 Apr 2006 13:29:26 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Subject: I2C: i2c-piix4: Fix typo in documentation
+Message-Id: <20060425132926.538d84ed.khali@linux-fr.org>
+Content-Disposition: inline; filename=i2c-piix4-fix-typo-in-documentation.patch
+
+Fix i2c-piix4 documentation typo.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/i2c/busses/i2c-piix4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-piix4
++++ gregkh-2.6/Documentation/i2c/busses/i2c-piix4
+@@ -63,7 +63,7 @@ The PIIX4E is just an new version of the
+ The PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use
+ this driver on those mainboards.
+
+-The ServerWorks Southbridges, the Intel 440MX, and the Victory766 are
++The ServerWorks Southbridges, the Intel 440MX, and the Victory66 are
+ identical to the PIIX4 in I2C/SMBus support.
+
+ If you own Force CPCI735 motherboard or other OSB4 based systems you may need
diff --git a/i2c/i2c-piix4-improve-ibm-error-message.patch b/i2c/i2c-piix4-improve-ibm-error-message.patch
new file mode 100644
index 0000000000000..9bd994632f0e6
--- /dev/null
+++ b/i2c/i2c-piix4-improve-ibm-error-message.patch
@@ -0,0 +1,54 @@
+From khali@linux-fr.org Tue Apr 25 04:37:23 2006
+Date: Tue, 25 Apr 2006 13:37:25 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Subject: I2C: i2c-piix4: Document the IBM problem more clearly
+Message-Id: <20060425133725.ef4eadd2.khali@linux-fr.org>
+Content-Disposition: inline; filename=i2c-piix4-improve-ibm-error-message.patch
+
+Properly document on which systems the i2c-piix4 SMBus driver will
+refuse to load. Hopefully this will make it clearer for users, which
+were often wondering why their destop or server systems were detected
+as laptops.
+
+Closes bug #6429.
+
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/i2c/busses/i2c-piix4 | 14 ++++++++++++++
+ drivers/i2c/busses/i2c-piix4.c | 2 +-
+ 2 files changed, 15 insertions(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-piix4
++++ gregkh-2.6/Documentation/i2c/busses/i2c-piix4
+@@ -82,3 +82,17 @@ the SMI mode.
+
+ Please note that you don't need to do that in all cases, just when the SMBus is
+ not working properly.
++
++
++Hardware-specific issues
++------------------------
++
++This driver will refuse to load on IBM systems with an Intel PIIX4 SMBus.
++Some of these machines have an RFID EEPROM (24RF08) connected to the SMBus,
++which can easily get corrupted due to a state machine bug. These are mostly
++Thinkpad laptops, but desktop systems may also be affected. We have no list
++of all affected systems, so the only safe solution was to prevent access to
++the SMBus on all IBM systems (detected using DMI data.)
++
++For additional information, read:
++http://www2.lm-sensors.nu/~lm78/cvs/lm_sensors2/README.thinkpad
+--- gregkh-2.6.orig/drivers/i2c/busses/i2c-piix4.c
++++ gregkh-2.6/drivers/i2c/busses/i2c-piix4.c
+@@ -130,7 +130,7 @@ static int __devinit piix4_setup(struct
+ /* Don't access SMBus on IBM systems which get corrupted eeproms */
+ if (dmi_check_system(piix4_dmi_table) &&
+ PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
+- dev_err(&PIIX4_dev->dev, "IBM Laptop detected; this module "
++ dev_err(&PIIX4_dev->dev, "IBM system detected; this module "
+ "may corrupt your serial eeprom! Refusing to load "
+ "module!\n");
+ return -EPERM;
diff --git a/i2c/i2c-piix4-remove-fix_hstcfg-parameter.patch b/i2c/i2c-piix4-remove-fix_hstcfg-parameter.patch
new file mode 100644
index 0000000000000..9ffcf1214073d
--- /dev/null
+++ b/i2c/i2c-piix4-remove-fix_hstcfg-parameter.patch
@@ -0,0 +1,116 @@
+From khali@linux-fr.org Tue Apr 25 04:06:58 2006
+Date: Tue, 25 Apr 2006 13:06:41 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Cc: Rudolf Marek <r.marek@sh.cvut.cz>
+Subject: I2C: i2c-piix4: Remove the fix_hstcfg parameter
+Message-Id: <20060425130641.892e4179.khali@linux-fr.org>
+Content-Disposition: inline; filename=i2c-piix4-remove-fix_hstcfg-parameter.patch
+
+From: Rudolf Marek <r.marek@sh.cvut.cz>
+
+This patch removes the fix_hstcfg option from the driver and related
+SMBus Interrupt Select register magic because now we know what are
+valid values for this register. This patch updates the documentation
+and adds new IRQ mode check so we are sure not to miss any new
+"unusual" value.
+
+The PCI quirk for users of fix_hstcfg was not developed because the
+chipset lacks of subsystem ID registers and DMI is stated "To be
+filled". Impact to existing systems is minimal because the problem
+showed up on motherboards like 10 years back. On the other hand users
+of newer Serverworks and HT1000 systems won't be misleaded by the
+message suggesting to try the fix_hstcfg any more.
+
+Signed-off-by: Rudolf Marek <r.marek@sh.cvut.cz>
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/i2c/busses/i2c-piix4 | 22 ++++++++++++++++------
+ drivers/i2c/busses/i2c-piix4.c | 25 +------------------------
+ 2 files changed, 17 insertions(+), 30 deletions(-)
+
+--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-piix4
++++ gregkh-2.6/Documentation/i2c/busses/i2c-piix4
+@@ -23,8 +23,6 @@ Module Parameters
+ Forcibly enable the PIIX4. DANGEROUS!
+ * force_addr: int
+ Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
+-* fix_hstcfg: int
+- Fix config register. Needed on some boards (Force CPCI735).
+
+
+ Description
+@@ -68,7 +66,19 @@ this driver on those mainboards.
+ The ServerWorks Southbridges, the Intel 440MX, and the Victory766 are
+ identical to the PIIX4 in I2C/SMBus support.
+
+-A few OSB4 southbridges are known to be misconfigured by the BIOS. In this
+-case, you have you use the fix_hstcfg module parameter. Do not use it
+-unless you know you have to, because in some cases it also breaks
+-configuration on southbridges that don't need it.
++If you own Force CPCI735 motherboard or other OSB4 based systems you may need
++to change the SMBus Interrupt Select register so the SMBus controller uses
++the SMI mode.
++
++1) Use lspci command and locate the PCI device with the SMBus controller:
++ 00:0f.0 ISA bridge: ServerWorks OSB4 South Bridge (rev 4f)
++ The line may vary for different chipsets. Please consult the driver source
++ for all possible PCI ids (and lspci -n to match them). Lets assume the
++ device is located at 00:0f.0.
++2) Now you just need to change the value in 0xD2 register. Get it first with
++ command: lspci -xxx -s 00:0f.0
++ If the value is 0x3 then you need to change it to 0x1
++ setpci -s 00:0f.0 d2.b=1
++
++Please note that you don't need to do that in all cases, just when the SMBus is
++not working properly.
+--- gregkh-2.6.orig/drivers/i2c/busses/i2c-piix4.c
++++ gregkh-2.6/drivers/i2c/busses/i2c-piix4.c
+@@ -102,13 +102,6 @@ MODULE_PARM_DESC(force_addr,
+ "Forcibly enable the PIIX4 at the given address. "
+ "EXTREMELY DANGEROUS!");
+
+-/* If fix_hstcfg is set to anything different from 0, we reset one of the
+- registers to be a valid value. */
+-static int fix_hstcfg;
+-module_param (fix_hstcfg, int, 0);
+-MODULE_PARM_DESC(fix_hstcfg,
+- "Fix config register. Needed on some boards (Force CPCI735).");
+-
+ static int piix4_transaction(void);
+
+ static unsigned short piix4_smba;
+@@ -166,22 +159,6 @@ static int __devinit piix4_setup(struct
+
+ pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
+
+- /* Some BIOS will set up the chipset incorrectly and leave a register
+- in an undefined state (causing I2C to act very strangely). */
+- if (temp & 0x02) {
+- if (fix_hstcfg) {
+- dev_info(&PIIX4_dev->dev, "Working around buggy BIOS "
+- "(I2C)\n");
+- temp &= 0xfd;
+- pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp);
+- } else {
+- dev_info(&PIIX4_dev->dev, "Unusual config register "
+- "value\n");
+- dev_info(&PIIX4_dev->dev, "Try using fix_hstcfg=1 if "
+- "you experience problems\n");
+- }
+- }
+-
+ /* If force_addr is set, we program the new address here. Just to make
+ sure, we disable the PIIX4 first. */
+ if (force_addr) {
+@@ -214,7 +191,7 @@ static int __devinit piix4_setup(struct
+ }
+ }
+
+- if ((temp & 0x0E) == 8)
++ if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2))
+ dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n");
+ else if ((temp & 0x0E) == 0)
+ dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n");
diff --git a/i2c/rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch b/i2c/rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch
new file mode 100644
index 0000000000000..01b879a361b01
--- /dev/null
+++ b/i2c/rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch
@@ -0,0 +1,505 @@
+From khali@linux-fr.org Tue Apr 25 04:06:58 2006
+Date: Tue, 25 Apr 2006 13:04:54 +0200
+From: Jean Delvare <khali@linux-fr.org>
+To: Greg KH <greg@kroah.com>
+Cc: "Mark A. Greer" <mgreer@mvista.com>
+Subject: I2C: m41t00: Add support for the ST M41T81 and M41T85
+Message-Id: <20060425130454.f86d68db.khali@linux-fr.org>
+Content-Disposition: inline; filename=rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch
+
+From: "Mark A. Greer" <mgreer@mvista.com>
+
+This patch adds support for the ST m41t81 and m41t85 i2c rtc chips
+to the existing m41t00 driver.
+
+Since there is no way to reliably determine what type of rtc chip
+is in use, the chip type is passed in via platform_data. The i2c
+address and square wave frequency are passed in via platform_data
+as well. To accommodate the use of platform_data, a new header
+file include/linux/m41t00.h has been added.
+
+The m41t81 and m41t85 chips halt the updating of their time registers
+while they are being accessed. They resume when a stop condition
+exists on the i2c bus or when non-time related regs are accessed.
+To make the best use of that facility and to make more efficient
+use of the i2c bus, this patch replaces multiple i2c_smbus_xxx calls
+with a single i2c_transfer call.
+
+Signed-off-by: Mark A. Greer <mgreer@mvista.com>
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/i2c/chips/m41t00.c | 309 ++++++++++++++++++++++++++++++++++-----------
+ include/linux/m41t00.h | 50 +++++++
+ 2 files changed, 289 insertions(+), 70 deletions(-)
+
+--- gregkh-2.6.orig/drivers/i2c/chips/m41t00.c
++++ gregkh-2.6/drivers/i2c/chips/m41t00.c
+@@ -1,9 +1,9 @@
+ /*
+- * I2C client/driver for the ST M41T00 Real-Time Clock chip.
++ * I2C client/driver for the ST M41T00 family of i2c rtc chips.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+- * 2005 (c) MontaVista Software, Inc. This file is licensed under
++ * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+@@ -19,21 +19,17 @@
+ #include <linux/i2c.h>
+ #include <linux/rtc.h>
+ #include <linux/bcd.h>
+-#include <linux/mutex.h>
+ #include <linux/workqueue.h>
+-
++#include <linux/platform_device.h>
++#include <linux/m41t00.h>
+ #include <asm/time.h>
+ #include <asm/rtc.h>
+
+-#define M41T00_DRV_NAME "m41t00"
+-
+-static DEFINE_MUTEX(m41t00_mutex);
+-
+ static struct i2c_driver m41t00_driver;
+ static struct i2c_client *save_client;
+
+ static unsigned short ignore[] = { I2C_CLIENT_END };
+-static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
++static unsigned short normal_addr[] = { I2C_CLIENT_END, I2C_CLIENT_END };
+
+ static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_addr,
+@@ -41,34 +37,92 @@ static struct i2c_client_address_data ad
+ .ignore = ignore,
+ };
+
++struct m41t00_chip_info {
++ u8 type;
++ char *name;
++ u8 read_limit;
++ u8 sec; /* Offsets for chip regs */
++ u8 min;
++ u8 hour;
++ u8 day;
++ u8 mon;
++ u8 year;
++ u8 alarm_mon;
++ u8 alarm_hour;
++ u8 sqw;
++ u8 sqw_freq;
++};
++
++static struct m41t00_chip_info m41t00_chip_info_tbl[] = {
++ {
++ .type = M41T00_TYPE_M41T00,
++ .name = "m41t00",
++ .read_limit = 5,
++ .sec = 0,
++ .min = 1,
++ .hour = 2,
++ .day = 4,
++ .mon = 5,
++ .year = 6,
++ },
++ {
++ .type = M41T00_TYPE_M41T81,
++ .name = "m41t81",
++ .read_limit = 1,
++ .sec = 1,
++ .min = 2,
++ .hour = 3,
++ .day = 5,
++ .mon = 6,
++ .year = 7,
++ .alarm_mon = 0xa,
++ .alarm_hour = 0xc,
++ .sqw = 0x13,
++ },
++ {
++ .type = M41T00_TYPE_M41T85,
++ .name = "m41t85",
++ .read_limit = 1,
++ .sec = 1,
++ .min = 2,
++ .hour = 3,
++ .day = 5,
++ .mon = 6,
++ .year = 7,
++ .alarm_mon = 0xa,
++ .alarm_hour = 0xc,
++ .sqw = 0x13,
++ },
++};
++static struct m41t00_chip_info *m41t00_chip;
++
+ ulong
+ m41t00_get_rtc_time(void)
+ {
+ s32 sec, min, hour, day, mon, year;
+ s32 sec1, min1, hour1, day1, mon1, year1;
+- ulong limit = 10;
++ u8 reads = 0;
++ u8 buf[8], msgbuf[1] = { 0 }; /* offset into rtc's regs */
++ struct i2c_msg msgs[] = {
++ {
++ .addr = save_client->addr,
++ .flags = 0,
++ .len = 1,
++ .buf = msgbuf,
++ },
++ {
++ .addr = save_client->addr,
++ .flags = I2C_M_RD,
++ .len = 8,
++ .buf = buf,
++ },
++ };
+
+ sec = min = hour = day = mon = year = 0;
+- sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
+
+- mutex_lock(&m41t00_mutex);
+ do {
+- if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0)
+- && ((min = i2c_smbus_read_byte_data(save_client, 1))
+- >= 0)
+- && ((hour = i2c_smbus_read_byte_data(save_client, 2))
+- >= 0)
+- && ((day = i2c_smbus_read_byte_data(save_client, 4))
+- >= 0)
+- && ((mon = i2c_smbus_read_byte_data(save_client, 5))
+- >= 0)
+- && ((year = i2c_smbus_read_byte_data(save_client, 6))
+- >= 0)
+- && ((sec == sec1) && (min == min1) && (hour == hour1)
+- && (day == day1) && (mon == mon1)
+- && (year == year1)))
+-
+- break;
++ if (i2c_transfer(save_client->adapter, msgs, 2) < 0)
++ goto read_err;
+
+ sec1 = sec;
+ min1 = min;
+@@ -76,21 +130,21 @@ m41t00_get_rtc_time(void)
+ day1 = day;
+ mon1 = mon;
+ year1 = year;
+- } while (--limit > 0);
+- mutex_unlock(&m41t00_mutex);
+-
+- if (limit == 0) {
+- dev_warn(&save_client->dev,
+- "m41t00: can't read rtc chip\n");
+- sec = min = hour = day = mon = year = 0;
+- }
+
+- sec &= 0x7f;
+- min &= 0x7f;
+- hour &= 0x3f;
+- day &= 0x3f;
+- mon &= 0x1f;
+- year &= 0xff;
++ sec = buf[m41t00_chip->sec] & 0x7f;
++ min = buf[m41t00_chip->min] & 0x7f;
++ hour = buf[m41t00_chip->hour] & 0x3f;
++ day = buf[m41t00_chip->day] & 0x3f;
++ mon = buf[m41t00_chip->mon] & 0x1f;
++ year = buf[m41t00_chip->year];
++ } while ((++reads < m41t00_chip->read_limit) && ((sec != sec1)
++ || (min != min1) || (hour != hour1) || (day != day1)
++ || (mon != mon1) || (year != year1)));
++
++ if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1)
++ || (hour != hour1) || (day != day1) || (mon != mon1)
++ || (year != year1)))
++ goto read_err;
+
+ sec = BCD2BIN(sec);
+ min = BCD2BIN(min);
+@@ -104,40 +158,60 @@ m41t00_get_rtc_time(void)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
++
++read_err:
++ dev_err(&save_client->dev, "m41t00_get_rtc_time: Read error\n");
++ return 0;
+ }
++EXPORT_SYMBOL_GPL(m41t00_get_rtc_time);
+
+ static void
+ m41t00_set(void *arg)
+ {
+ struct rtc_time tm;
+- ulong nowtime = *(ulong *)arg;
++ int nowtime = *(int *)arg;
++ s32 sec, min, hour, day, mon, year;
++ u8 wbuf[9], *buf = &wbuf[1], msgbuf[1] = { 0 };
++ struct i2c_msg msgs[] = {
++ {
++ .addr = save_client->addr,
++ .flags = 0,
++ .len = 1,
++ .buf = msgbuf,
++ },
++ {
++ .addr = save_client->addr,
++ .flags = I2C_M_RD,
++ .len = 8,
++ .buf = buf,
++ },
++ };
+
+ to_tm(nowtime, &tm);
+ tm.tm_year = (tm.tm_year - 1900) % 100;
+
+- tm.tm_sec = BIN2BCD(tm.tm_sec);
+- tm.tm_min = BIN2BCD(tm.tm_min);
+- tm.tm_hour = BIN2BCD(tm.tm_hour);
+- tm.tm_mon = BIN2BCD(tm.tm_mon);
+- tm.tm_mday = BIN2BCD(tm.tm_mday);
+- tm.tm_year = BIN2BCD(tm.tm_year);
+-
+- mutex_lock(&m41t00_mutex);
+- if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
+- || (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
+- < 0)
+- || (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x3f)
+- < 0)
+- || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x3f)
+- < 0)
+- || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x1f)
+- < 0)
+- || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0xff)
+- < 0))
++ sec = BIN2BCD(tm.tm_sec);
++ min = BIN2BCD(tm.tm_min);
++ hour = BIN2BCD(tm.tm_hour);
++ day = BIN2BCD(tm.tm_mday);
++ mon = BIN2BCD(tm.tm_mon);
++ year = BIN2BCD(tm.tm_year);
++
++ /* Read reg values into buf[0..7]/wbuf[1..8] */
++ if (i2c_transfer(save_client->adapter, msgs, 2) < 0) {
++ dev_err(&save_client->dev, "m41t00_set: Read error\n");
++ return;
++ }
+
+- dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
++ wbuf[0] = 0; /* offset into rtc's regs */
++ buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f);
++ buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f);
++ buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f);
++ buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f);
++ buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f);
+
+- mutex_unlock(&m41t00_mutex);
++ if (i2c_master_send(save_client, wbuf, 9) < 0)
++ dev_err(&save_client->dev, "m41t00_set: Write error\n");
+ }
+
+ static ulong new_time;
+@@ -156,6 +230,48 @@ m41t00_set_rtc_time(ulong nowtime)
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(m41t00_set_rtc_time);
++
++/*
++ *****************************************************************************
++ *
++ * platform_data Driver Interface
++ *
++ *****************************************************************************
++ */
++static int __init
++m41t00_platform_probe(struct platform_device *pdev)
++{
++ struct m41t00_platform_data *pdata;
++ int i;
++
++ if (pdev && (pdata = pdev->dev.platform_data)) {
++ normal_addr[0] = pdata->i2c_addr;
++
++ for (i=0; i<ARRAY_SIZE(m41t00_chip_info_tbl); i++)
++ if (m41t00_chip_info_tbl[i].type == pdata->type) {
++ m41t00_chip = &m41t00_chip_info_tbl[i];
++ m41t00_chip->sqw_freq = pdata->sqw_freq;
++ return 0;
++ }
++ }
++ return -ENODEV;
++}
++
++static int __exit
++m41t00_platform_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver m41t00_platform_driver = {
++ .probe = m41t00_platform_probe,
++ .remove = m41t00_platform_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = M41T00_DRV_NAME,
++ },
++};
+
+ /*
+ *****************************************************************************
+@@ -170,23 +286,71 @@ m41t00_probe(struct i2c_adapter *adap, i
+ struct i2c_client *client;
+ int rc;
+
++ if (!i2c_check_functionality(adap, I2C_FUNC_I2C
++ | I2C_FUNC_SMBUS_BYTE_DATA))
++ return 0;
++
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+- strlcpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE);
++ strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE);
+ client->addr = addr;
+ client->adapter = adap;
+ client->driver = &m41t00_driver;
+
+- if ((rc = i2c_attach_client(client)) != 0) {
+- kfree(client);
+- return rc;
++ if ((rc = i2c_attach_client(client)))
++ goto attach_err;
++
++ if (m41t00_chip->type != M41T00_TYPE_M41T00) {
++ /* If asked, disable SQW, set SQW frequency & re-enable */
++ if (m41t00_chip->sqw_freq)
++ if (((rc = i2c_smbus_read_byte_data(client,
++ m41t00_chip->alarm_mon)) < 0)
++ || ((rc = i2c_smbus_write_byte_data(client,
++ m41t00_chip->alarm_mon, rc & ~0x40)) <0)
++ || ((rc = i2c_smbus_write_byte_data(client,
++ m41t00_chip->sqw,
++ m41t00_chip->sqw_freq)) < 0)
++ || ((rc = i2c_smbus_write_byte_data(client,
++ m41t00_chip->alarm_mon, rc | 0x40)) <0))
++ goto sqw_err;
++
++ /* Make sure HT (Halt Update) bit is cleared */
++ if ((rc = i2c_smbus_read_byte_data(client,
++ m41t00_chip->alarm_hour)) < 0)
++ goto ht_err;
++
++ if (rc & 0x40)
++ if ((rc = i2c_smbus_write_byte_data(client,
++ m41t00_chip->alarm_hour, rc & ~0x40))<0)
++ goto ht_err;
+ }
+
+- m41t00_wq = create_singlethread_workqueue("m41t00");
++ /* Make sure ST (stop) bit is cleared */
++ if ((rc = i2c_smbus_read_byte_data(client, m41t00_chip->sec)) < 0)
++ goto st_err;
++
++ if (rc & 0x80)
++ if ((rc = i2c_smbus_write_byte_data(client, m41t00_chip->sec,
++ rc & ~0x80)) < 0)
++ goto st_err;
++
++ m41t00_wq = create_singlethread_workqueue(m41t00_chip->name);
+ save_client = client;
+ return 0;
++
++st_err:
++ dev_err(&client->dev, "m41t00_probe: Can't clear ST bit\n");
++ goto attach_err;
++ht_err:
++ dev_err(&client->dev, "m41t00_probe: Can't clear HT bit\n");
++ goto attach_err;
++sqw_err:
++ dev_err(&client->dev, "m41t00_probe: Can't set SQW Frequency\n");
++attach_err:
++ kfree(client);
++ return rc;
+ }
+
+ static int
+@@ -219,13 +383,18 @@ static struct i2c_driver m41t00_driver =
+ static int __init
+ m41t00_init(void)
+ {
+- return i2c_add_driver(&m41t00_driver);
++ int rc;
++
++ if (!(rc = platform_driver_register(&m41t00_platform_driver)))
++ rc = i2c_add_driver(&m41t00_driver);
++ return rc;
+ }
+
+ static void __exit
+ m41t00_exit(void)
+ {
+ i2c_del_driver(&m41t00_driver);
++ platform_driver_unregister(&m41t00_platform_driver);
+ }
+
+ module_init(m41t00_init);
+--- /dev/null
++++ gregkh-2.6/include/linux/m41t00.h
+@@ -0,0 +1,50 @@
++/*
++ * Definitions for the ST M41T00 family of i2c rtc chips.
++ *
++ * Author: Mark A. Greer <mgreer@mvista.com>
++ *
++ * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ */
++
++#ifndef _M41T00_H
++#define _M41T00_H
++
++#define M41T00_DRV_NAME "m41t00"
++#define M41T00_I2C_ADDR 0x68
++
++#define M41T00_TYPE_M41T00 0
++#define M41T00_TYPE_M41T81 81
++#define M41T00_TYPE_M41T85 85
++
++struct m41t00_platform_data {
++ u8 type;
++ u8 i2c_addr;
++ u8 sqw_freq;
++};
++
++/* SQW output disabled, this is default value by power on */
++#define M41T00_SQW_DISABLE (0)
++
++#define M41T00_SQW_32KHZ (1<<4) /* 32.768 KHz */
++#define M41T00_SQW_8KHZ (2<<4) /* 8.192 KHz */
++#define M41T00_SQW_4KHZ (3<<4) /* 4.096 KHz */
++#define M41T00_SQW_2KHZ (4<<4) /* 2.048 KHz */
++#define M41T00_SQW_1KHZ (5<<4) /* 1.024 KHz */
++#define M41T00_SQW_512HZ (6<<4) /* 512 Hz */
++#define M41T00_SQW_256HZ (7<<4) /* 256 Hz */
++#define M41T00_SQW_128HZ (8<<4) /* 128 Hz */
++#define M41T00_SQW_64HZ (9<<4) /* 64 Hz */
++#define M41T00_SQW_32HZ (10<<4) /* 32 Hz */
++#define M41T00_SQW_16HZ (11<<4) /* 16 Hz */
++#define M41T00_SQW_8HZ (12<<4) /* 8 Hz */
++#define M41T00_SQW_4HZ (13<<4) /* 4 Hz */
++#define M41T00_SQW_2HZ (14<<4) /* 2 Hz */
++#define M41T00_SQW_1HZ (15<<4) /* 1 Hz */
++
++extern ulong m41t00_get_rtc_time(void);
++extern int m41t00_set_rtc_time(ulong nowtime);
++
++#endif /* _M41T00_H */