diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-04-25 15:26:28 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-04-25 15:26:28 -0700 |
commit | 393db0324a430e100971ead4210d1c7870509b7c (patch) | |
tree | 10a2cded9e03b7ea51fdac154328cde938e411aa /i2c | |
parent | efe9e8261c1359bdb9ae90661eff88c8e740d3f9 (diff) | |
download | patches-393db0324a430e100971ead4210d1c7870509b7c.tar.gz |
more patches added
Diffstat (limited to 'i2c')
-rw-r--r-- | i2c/hwmon-hdaps-update-id-list.patch | 55 | ||||
-rw-r--r-- | i2c/hwmon-improve-Kconfig-help.patch | 78 | ||||
-rw-r--r-- | i2c/hwmon-lm83-documentation-update.patch | 28 | ||||
-rw-r--r-- | i2c/hwmon-vid-mask-per-vrm.patch | 69 | ||||
-rw-r--r-- | i2c/hwmon-w83791d-new-driver.patch | 1429 | ||||
-rw-r--r-- | i2c/i2c-nforce2-add-mcp51-mcp55-support.patch | 131 | ||||
-rw-r--r-- | i2c/i2c-piix4-fix-typo-in-documentation.patch | 28 | ||||
-rw-r--r-- | i2c/i2c-piix4-improve-ibm-error-message.patch | 54 | ||||
-rw-r--r-- | i2c/i2c-piix4-remove-fix_hstcfg-parameter.patch | 116 | ||||
-rw-r--r-- | i2c/rtc-add-support-for-m41t81-m41t85-chips-to-m41t00-driver.patch | 505 |
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 */ |