diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-04-03 15:02:22 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-04-03 15:02:22 -0700 |
commit | f9b3d7ae998e086d7247a662e656c40f4de0a18b (patch) | |
tree | 45e723c9aa041ae729b47c429ec3d6bfa54c6660 /i2c | |
parent | 158d7a505ae2253c37c0ffd4a9460cbcf7ef612f (diff) | |
download | patches-f9b3d7ae998e086d7247a662e656c40f4de0a18b.tar.gz |
new i2c patches
Diffstat (limited to 'i2c')
-rw-r--r-- | i2c/hwmon-f71805f-no-global-resource.patch | 56 | ||||
-rw-r--r-- | i2c/hwmon-lm83-add-lm82-support.patch | 180 | ||||
-rw-r--r-- | i2c/hwmon-smsc47m192-new-driver.patch | 843 | ||||
-rw-r--r-- | i2c/hwmon-sysfs-interface-individual-alarm-files.patch | 145 | ||||
-rw-r--r-- | i2c/hwmon-w83627ehf-add-alarms.patch | 132 | ||||
-rw-r--r-- | i2c/hwmon-w83627ehf-add-voltages.patch | 198 | ||||
-rw-r--r-- | i2c/hwmon-w83792d-quiet-on-misdetection.patch | 48 | ||||
-rw-r--r-- | i2c/i2c-parport-require-type-parameter.patch | 127 | ||||
-rw-r--r-- | i2c/i2c-piix4-add-ati-smbus-support.patch | 86 | ||||
-rw-r--r-- | i2c/i2c-sis96x-remove-init-log-message.patch | 46 | ||||
-rw-r--r-- | i2c/rtc-ds1374-convert-tasklet-to-workqueue.patch | 83 | ||||
-rw-r--r-- | i2c/rtc-m41t00-driver-cleanup.patch | 145 | ||||
-rw-r--r-- | i2c/rtc-m41t00-driver-should-use-workqueue-instead-of-tasklet.patch | 84 |
13 files changed, 2173 insertions, 0 deletions
diff --git a/i2c/hwmon-f71805f-no-global-resource.patch b/i2c/hwmon-f71805f-no-global-resource.patch new file mode 100644 index 0000000000000..0a17ed3c95240 --- /dev/null +++ b/i2c/hwmon-f71805f-no-global-resource.patch @@ -0,0 +1,56 @@ +From khali@linux-fr.org Thu Mar 23 07:39:52 2006 +Date: Thu, 23 Mar 2006 16:40:23 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: [PATCH 06/10] f71805f: Resource needs not be global +Message-Id: <20060323164023.f5380bf9.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-f71805f-no-global-resource.patch + +The F71805F I/O resource structure needs not be a global variable, +as the platform core allocs its own copy of it anyway. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/f71805f.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/f71805f.c ++++ gregkh-2.6/drivers/hwmon/f71805f.c +@@ -99,10 +99,6 @@ superio_exit(int base) + #define ADDR_REG_OFFSET 0 + #define DATA_REG_OFFSET 1 + +-static struct resource f71805f_resource __initdata = { +- .flags = IORESOURCE_IO, +-}; +- + /* + * Registers + */ +@@ -782,6 +778,11 @@ static struct platform_driver f71805f_dr + + static int __init f71805f_device_add(unsigned short address) + { ++ struct resource res = { ++ .start = address, ++ .end = address + REGION_LENGTH - 1, ++ .flags = IORESOURCE_IO, ++ }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); +@@ -791,10 +792,8 @@ static int __init f71805f_device_add(uns + goto exit; + } + +- f71805f_resource.start = address; +- f71805f_resource.end = address + REGION_LENGTH - 1; +- f71805f_resource.name = pdev->name; +- err = platform_device_add_resources(pdev, &f71805f_resource, 1); ++ res.name = pdev->name; ++ err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); diff --git a/i2c/hwmon-lm83-add-lm82-support.patch b/i2c/hwmon-lm83-add-lm82-support.patch new file mode 100644 index 0000000000000..765d97137985b --- /dev/null +++ b/i2c/hwmon-lm83-add-lm82-support.patch @@ -0,0 +1,180 @@ +From khali@linux-fr.org Thu Mar 23 07:19:16 2006 +Date: Thu, 23 Mar 2006 16:19:49 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Jordan Crouse <jordan.crouse@amd.com> +Subject: [PATCH 01/10] lm83: Add LM82 support +Message-Id: <20060323161949.e6dd074b.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-lm83-add-lm82-support.patch + +From: Jordan Crouse <jordan.crouse@amd.com> + +Add LM82 temperature sensor support (similar to the LM83, +but less featureful). + +Signed-off-by: Jordan Crouse <jordan.crouse@amd.com> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/lm83 | 16 +++++++++++---- + drivers/hwmon/Kconfig | 4 +-- + drivers/hwmon/lm83.c | 50 ++++++++++++++++++++++++++++++++++------------- + 3 files changed, 51 insertions(+), 19 deletions(-) + +--- gregkh-2.6.orig/Documentation/hwmon/lm83 ++++ gregkh-2.6/Documentation/hwmon/lm83 +@@ -7,6 +7,10 @@ Supported chips: + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM83.html ++ * National Semiconductor LM82 ++ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e ++ Datasheet: Publicly available at the National Semiconductor website ++ http://www.national.com/pf/LM/LM82.html + + + Author: Jean Delvare <khali@linux-fr.org> +@@ -15,10 +19,11 @@ Description + ----------- + + The LM83 is a digital temperature sensor. It senses its own temperature as +-well as the temperature of up to three external diodes. It is compatible +-with many other devices such as the LM84 and all other ADM1021 clones. +-The main difference between the LM83 and the LM84 in that the later can +-only sense the temperature of one external diode. ++well as the temperature of up to three external diodes. The LM82 is ++a stripped down version of the LM83 that only supports one external diode. ++Both are compatible with many other devices such as the LM84 and all ++other ADM1021 clones. The main difference between the LM83 and the LM84 ++in that the later can only sense the temperature of one external diode. + + Using the adm1021 driver for a LM83 should work, but only two temperatures + will be reported instead of four. +@@ -36,6 +41,9 @@ Unconfirmed motherboards: + Iwill MPX2 + Soltek SL-75DRV5 + ++The LM82 is confirmed to have been found on most AMD Geode reference ++designs and test platforms. ++ + The driver has been successfully tested by Magnus Forsström, who I'd + like to thank here. More testers will be of course welcome. + +--- gregkh-2.6.orig/drivers/hwmon/Kconfig ++++ gregkh-2.6/drivers/hwmon/Kconfig +@@ -236,11 +236,11 @@ config SENSORS_LM80 + will be called lm80. + + config SENSORS_LM83 +- tristate "National Semiconductor LM83" ++ tristate "National Semiconductor LM83 and compatibles" + depends on HWMON && I2C + help + If you say yes here you get support for National Semiconductor +- LM83 sensor chips. ++ LM82 and LM83 sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm83. +--- gregkh-2.6.orig/drivers/hwmon/lm83.c ++++ gregkh-2.6/drivers/hwmon/lm83.c +@@ -12,6 +12,10 @@ + * Since the datasheet omits to give the chip stepping code, I give it + * here: 0x03 (at register 0xff). + * ++ * Also supports the LM82 temp sensor, which is basically a stripped down ++ * model of the LM83. Datasheet is here: ++ * http://www.national.com/pf/LM/LM82.html ++ * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or +@@ -52,7 +56,7 @@ static unsigned short normal_i2c[] = { 0 + * Insmod parameters + */ + +-I2C_CLIENT_INSMOD_1(lm83); ++I2C_CLIENT_INSMOD_2(lm83, lm82); + + /* + * The LM83 registers +@@ -283,6 +287,9 @@ static int lm83_detect(struct i2c_adapte + if (man_id == 0x01) { /* National Semiconductor */ + if (chip_id == 0x03) { + kind = lm83; ++ } else ++ if (chip_id == 0x01) { ++ kind = lm82; + } + } + +@@ -296,6 +303,9 @@ static int lm83_detect(struct i2c_adapte + + if (kind == lm83) { + name = "lm83"; ++ } else ++ if (kind == lm82) { ++ name = "lm82"; + } + + /* We can fill in the remaining client fields */ +@@ -319,32 +329,46 @@ static int lm83_detect(struct i2c_adapte + goto exit_detach; + } + ++ /* ++ * The LM82 can only monitor one external diode which is ++ * at the same register as the LM83 temp3 entry - so we ++ * declare 1 and 3 common, and then 2 and 4 only for the LM83. ++ */ ++ + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, +- &sensor_dev_attr_temp2_input.dev_attr); +- device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_input.dev_attr); +- device_create_file(&new_client->dev, +- &sensor_dev_attr_temp4_input.dev_attr); ++ + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, +- &sensor_dev_attr_temp2_max.dev_attr); +- device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_max.dev_attr); +- device_create_file(&new_client->dev, +- &sensor_dev_attr_temp4_max.dev_attr); ++ + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_crit.dev_attr); + device_create_file(&new_client->dev, +- &sensor_dev_attr_temp2_crit.dev_attr); +- device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_crit.dev_attr); +- device_create_file(&new_client->dev, +- &sensor_dev_attr_temp4_crit.dev_attr); ++ + device_create_file(&new_client->dev, &dev_attr_alarms); + ++ if (kind == lm83) { ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp2_input.dev_attr); ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp4_input.dev_attr); ++ ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp2_max.dev_attr); ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp4_max.dev_attr); ++ ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp2_crit.dev_attr); ++ device_create_file(&new_client->dev, ++ &sensor_dev_attr_temp4_crit.dev_attr); ++ } ++ + return 0; + + exit_detach: diff --git a/i2c/hwmon-smsc47m192-new-driver.patch b/i2c/hwmon-smsc47m192-new-driver.patch new file mode 100644 index 0000000000000..9f5b8811ea3a5 --- /dev/null +++ b/i2c/hwmon-smsc47m192-new-driver.patch @@ -0,0 +1,843 @@ +From khali@linux-fr.org Thu Mar 23 07:36:55 2006 +Date: Thu, 23 Mar 2006 16:37:23 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Hartmut Rick <linux@rick.claranet.de> +Subject: [PATCH 04/10] smsc47m192: New hwmon driver for SMSC LPC47M192/997 +Message-Id: <20060323163723.46c3c92b.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-smsc47m192-new-driver.patch + +From: Hartmut Rick <linux@rick.claranet.de> + +New driver (smsc47m192) which supports voltage and temperature +measurement features of SMSC LPC47M192 and LPC47M997 chips. + +Signed-off-by: Hartmut Rick <linux@rick.claranet.de> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/smsc47m192 | 102 +++++ + Documentation/hwmon/sysfs-interface | 6 + drivers/hwmon/Kconfig | 23 + + drivers/hwmon/Makefile | 1 + drivers/hwmon/smsc47m192.c | 648 ++++++++++++++++++++++++++++++++++++ + 5 files changed, 779 insertions(+), 1 deletion(-) + +--- /dev/null ++++ gregkh-2.6/Documentation/hwmon/smsc47m192 +@@ -0,0 +1,102 @@ ++Kernel driver smsc47m192 ++======================== ++ ++Supported chips: ++ * SMSC LPC47M192 and LPC47M997 ++ Prefix: 'smsc47m192' ++ Addresses scanned: I2C 0x2c - 0x2d ++ Datasheet: The datasheet for LPC47M192 is publicly available from ++ http://www.smsc.com/ ++ The LPC47M997 is compatible for hardware monitoring. ++ ++Author: Hartmut Rick <linux@rick.claranet.de> ++ Special thanks to Jean Delvare for careful checking ++ of the code and many helpful comments and suggestions. ++ ++ ++Description ++----------- ++ ++This driver implements support for the hardware sensor capabilities ++of the SMSC LPC47M192 and LPC47M997 Super-I/O chips. ++ ++These chips support 3 temperature channels and 8 voltage inputs ++as well as CPU voltage VID input. ++ ++They do also have fan monitoring and control capabilities, but the ++these features are accessed via ISA bus and are not supported by this ++driver. Use the 'smsc47m1' driver for fan monitoring and control. ++ ++Voltages and temperatures are measured by an 8-bit ADC, the resolution ++of the temperatures is 1 bit per degree C. ++Voltages are scaled such that the nominal voltage corresponds to ++192 counts, i.e. 3/4 of the full range. Thus the available range for ++each voltage channel is 0V ... 255/192*(nominal voltage), the resolution ++is 1 bit per (nominal voltage)/192. ++Both voltage and temperature values are scaled by 1000, the sys files ++show voltages in mV and temperatures in units of 0.001 degC. ++ ++The +12V analog voltage input channel (in4_input) is multiplexed with ++bit 4 of the encoded CPU voltage. This means that you either get ++a +12V voltage measurement or a 5 bit CPU VID, but not both. ++The default setting is to use the pin as 12V input, and use only 4 bit VID. ++This driver assumes that the information in the configuration register ++is correct, i.e. that the BIOS has updated the configuration if ++the motherboard has this input wired to VID4. ++ ++The temperature and voltage readings are updated once every 1.5 seconds. ++Reading them more often repeats the same values. ++ ++ ++sysfs interface ++--------------- ++ ++in0_input - +2.5V voltage input ++in1_input - CPU voltage input (nominal 2.25V) ++in2_input - +3.3V voltage input ++in3_input - +5V voltage input ++in4_input - +12V voltage input (may be missing if used as VID4) ++in5_input - Vcc voltage input (nominal 3.3V) ++ This is the supply voltage of the sensor chip itself. ++in6_input - +1.5V voltage input ++in7_input - +1.8V voltage input ++ ++in[0-7]_min, ++in[0-7]_max - lower and upper alarm thresholds for in[0-7]_input reading ++ ++ All voltages are read and written in mV. ++ ++in[0-7]_alarm - alarm flags for voltage inputs ++ These files read '1' in case of alarm, '0' otherwise. ++ ++temp1_input - chip temperature measured by on-chip diode ++temp[2-3]_input - temperature measured by external diodes (one of these would ++ typically be wired to the diode inside the CPU) ++ ++temp[1-3]_min, ++temp[1-3]_max - lower and upper alarm thresholds for temperatures ++ ++temp[1-3]_offset - temperature offset registers ++ The chip adds the offsets stored in these registers to ++ the corresponding temperature readings. ++ Note that temp1 and temp2 offsets share the same register, ++ they cannot both be different from zero at the same time. ++ Writing a non-zero number to one of them will reset the other ++ offset to zero. ++ ++ All temperatures and offsets are read and written in ++ units of 0.001 degC. ++ ++temp[1-3]_alarm - alarm flags for temperature inputs, '1' in case of alarm, ++ '0' otherwise. ++temp[2-3]_input_fault - diode fault flags for temperature inputs 2 and 3. ++ A fault is detected if the two pins for the corresponding ++ sensor are open or shorted, or any of the two is shorted ++ to ground or Vcc. '1' indicates a diode fault. ++ ++cpu0_vid - CPU voltage as received from the CPU ++ ++vrm - CPU VID standard used for decoding CPU voltage ++ ++ The *_min, *_max, *_offset and vrm files can be read and ++ written, all others are read-only. +--- gregkh-2.6.orig/Documentation/hwmon/sysfs-interface ++++ gregkh-2.6/Documentation/hwmon/sysfs-interface +@@ -218,6 +218,12 @@ temp[1-2]_crit_hyst + from the critical value. + Read/Write value. + ++temp[1-4]_offset ++ Temperature offset which is added to the temperature reading ++ by the chip. ++ Unit: millidegree Celsius ++ Read/Write value. ++ + If there are multiple temperature sensors, temp1_* is + generally the sensor inside the chip itself, + reported as "motherboard temperature". temp2_* to +--- gregkh-2.6.orig/drivers/hwmon/Kconfig ++++ gregkh-2.6/drivers/hwmon/Kconfig +@@ -333,11 +333,32 @@ config SENSORS_SMSC47M1 + help + If you say yes here you get support for the integrated fan + monitoring and control capabilities of the SMSC LPC47B27x, +- LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 chips. ++ LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x, LPC47M192 and ++ LPC47M997 chips. ++ ++ The temperature and voltage sensor features of the LPC47M192 ++ and LPC47M997 are supported by another driver, select also ++ "SMSC LPC47M192 and compatibles" below for those. + + This driver can also be built as a module. If so, the module + will be called smsc47m1. + ++config SENSORS_SMSC47M192 ++ tristate "SMSC LPC47M192 and compatibles" ++ depends on HWMON && I2C && EXPERIMENTAL ++ select HWMON_VID ++ help ++ If you say yes here you get support for the temperature and ++ voltage sensors of the SMSC LPC47M192 and LPC47M997 chips. ++ ++ The fan monitoring and control capabilities of these chips ++ are supported by another driver, select ++ "SMSC LPC47M10x and compatibles" above. You need both drivers ++ if you want fan control and voltage/temperature sensor support. ++ ++ This driver can also be built as a module. If so, the module ++ will be called smsc47m192. ++ + config SENSORS_SMSC47B397 + tristate "SMSC LPC47B397-NC" + depends on HWMON && I2C && EXPERIMENTAL +--- gregkh-2.6.orig/drivers/hwmon/Makefile ++++ gregkh-2.6/drivers/hwmon/Makefile +@@ -40,6 +40,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360 + obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o + obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o + obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o ++obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o + obj-$(CONFIG_SENSORS_VIA686A) += via686a.o + obj-$(CONFIG_SENSORS_VT8231) += vt8231.o + obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o +--- /dev/null ++++ gregkh-2.6/drivers/hwmon/smsc47m192.c +@@ -0,0 +1,648 @@ ++/* ++ smsc47m192.c - Support for hardware monitoring block of ++ SMSC LPC47M192 and LPC47M997 Super I/O chips ++ ++ Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de> ++ ++ Derived from lm78.c and other chip drivers. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/jiffies.h> ++#include <linux/i2c.h> ++#include <linux/hwmon.h> ++#include <linux/hwmon-sysfs.h> ++#include <linux/hwmon-vid.h> ++#include <linux/err.h> ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; ++ ++/* Insmod parameters */ ++I2C_CLIENT_INSMOD_1(smsc47m192); ++ ++/* SMSC47M192 registers */ ++#define SMSC47M192_REG_IN(nr) ((nr)<6 ? (0x20 + (nr)) : \ ++ (0x50 + (nr) - 6)) ++#define SMSC47M192_REG_IN_MAX(nr) ((nr)<6 ? (0x2b + (nr) * 2) : \ ++ (0x54 + (((nr) - 6) * 2))) ++#define SMSC47M192_REG_IN_MIN(nr) ((nr)<6 ? (0x2c + (nr) * 2) : \ ++ (0x55 + (((nr) - 6) * 2))) ++static u8 SMSC47M192_REG_TEMP[3] = { 0x27, 0x26, 0x52 }; ++static u8 SMSC47M192_REG_TEMP_MAX[3] = { 0x39, 0x37, 0x58 }; ++static u8 SMSC47M192_REG_TEMP_MIN[3] = { 0x3A, 0x38, 0x59 }; ++#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr)==2 ? 0x1e : 0x1f) ++#define SMSC47M192_REG_ALARM1 0x41 ++#define SMSC47M192_REG_ALARM2 0x42 ++#define SMSC47M192_REG_VID 0x47 ++#define SMSC47M192_REG_VID4 0x49 ++#define SMSC47M192_REG_CONFIG 0x40 ++#define SMSC47M192_REG_SFR 0x4f ++#define SMSC47M192_REG_COMPANY_ID 0x3e ++#define SMSC47M192_REG_VERSION 0x3f ++ ++/* generalised scaling with integer rounding */ ++static inline int SCALE(long val, int mul, int div) ++{ ++ if (val < 0) ++ return (val * mul - div / 2) / div; ++ else ++ return (val * mul + div / 2) / div; ++} ++ ++/* Conversions */ ++ ++/* smsc47m192 internally scales voltage measurements */ ++static const u16 nom_mv[] = { 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 }; ++ ++static inline unsigned int IN_FROM_REG(u8 reg, int n) ++{ ++ return SCALE(reg, nom_mv[n], 192); ++} ++ ++static inline u8 IN_TO_REG(unsigned long val, int n) ++{ ++ return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); ++} ++ ++/* TEMP: 0.001 degC units (-128C to +127C) ++ REG: 1C/bit, two's complement */ ++static inline s8 TEMP_TO_REG(int val) ++{ ++ return SENSORS_LIMIT(SCALE(val, 1, 1000), -128000, 127000); ++} ++ ++static inline int TEMP_FROM_REG(s8 val) ++{ ++ return val * 1000; ++} ++ ++struct smsc47m192_data { ++ struct i2c_client client; ++ struct class_device *class_dev; ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[8]; /* Register value */ ++ u8 in_max[8]; /* Register value */ ++ u8 in_min[8]; /* Register value */ ++ s8 temp[3]; /* Register value */ ++ s8 temp_max[3]; /* Register value */ ++ s8 temp_min[3]; /* Register value */ ++ s8 temp_offset[3]; /* Register value */ ++ u16 alarms; /* Register encoding, combined */ ++ u8 vid; /* Register encoding, combined */ ++ u8 vrm; ++}; ++ ++static int smsc47m192_attach_adapter(struct i2c_adapter *adapter); ++static int smsc47m192_detect(struct i2c_adapter *adapter, int address, ++ int kind); ++static int smsc47m192_detach_client(struct i2c_client *client); ++static struct smsc47m192_data *smsc47m192_update_device(struct device *dev); ++ ++static struct i2c_driver smsc47m192_driver = { ++ .driver = { ++ .name = "smsc47m192", ++ }, ++ .attach_adapter = smsc47m192_attach_adapter, ++ .detach_client = smsc47m192_detach_client, ++}; ++ ++/* Voltages */ ++static ssize_t show_in(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr)); ++} ++ ++static ssize_t show_in_min(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr)); ++} ++ ++static ssize_t show_in_max(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr)); ++} ++ ++static ssize_t set_in_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); ++ int nr = sensor_attr->index; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ unsigned long val = simple_strtoul(buf, NULL, 10); ++ ++ down(&data->update_lock); ++ data->in_min[nr] = IN_TO_REG(val, nr); ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ up(&data->update_lock); ++ return count; ++} ++ ++static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int nr = sensor_attr->index; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ unsigned long val = simple_strtoul(buf, NULL, 10); ++ ++ down(&data->update_lock); ++ data->in_max[nr] = IN_TO_REG(val, nr); ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ up(&data->update_lock); ++ return count; ++} ++ ++#define show_in_offset(offset) \ ++static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ ++ show_in, NULL, offset); \ ++static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ ++ show_in_min, set_in_min, offset); \ ++static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ ++ show_in_max, set_in_max, offset); ++ ++show_in_offset(0) ++show_in_offset(1) ++show_in_offset(2) ++show_in_offset(3) ++show_in_offset(4) ++show_in_offset(5) ++show_in_offset(6) ++show_in_offset(7) ++ ++/* Temperatures */ ++static ssize_t show_temp(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); ++} ++ ++static ssize_t show_temp_min(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr])); ++} ++ ++static ssize_t show_temp_max(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr])); ++} ++ ++static ssize_t set_temp_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); ++ int nr = sensor_attr->index; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ long val = simple_strtol(buf, NULL, 10); ++ ++ down(&data->update_lock); ++ data->temp_min[nr] = TEMP_TO_REG(val); ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MIN[nr], ++ data->temp_min[nr]); ++ up(&data->update_lock); ++ return count; ++} ++ ++static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int nr = sensor_attr->index; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ long val = simple_strtol(buf, NULL, 10); ++ ++ down(&data->update_lock); ++ data->temp_max[nr] = TEMP_TO_REG(val); ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MAX[nr], ++ data->temp_max[nr]); ++ up(&data->update_lock); ++ return count; ++} ++ ++static ssize_t show_temp_offset(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_offset[nr])); ++} ++ ++static ssize_t set_temp_offset(struct device *dev, struct device_attribute ++ *attr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int nr = sensor_attr->index; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); ++ long val = simple_strtol(buf, NULL, 10); ++ ++ down(&data->update_lock); ++ data->temp_offset[nr] = TEMP_TO_REG(val); ++ if (nr>1) ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); ++ else if (data->temp_offset[nr] != 0) { ++ /* offset[0] and offset[1] share the same register, ++ SFR bit 4 activates offset[0] */ ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR, ++ (sfr & 0xef) | (nr==0 ? 0x10 : 0)); ++ data->temp_offset[1-nr] = 0; ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); ++ } else if ((sfr & 0x10) == (nr==0 ? 0x10 : 0)) ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_TEMP_OFFSET(nr), 0); ++ up(&data->update_lock); ++ return count; ++} ++ ++#define show_temp_index(index) \ ++static SENSOR_DEVICE_ATTR(temp##index##_input, S_IRUGO, \ ++ show_temp, NULL, index-1); \ ++static SENSOR_DEVICE_ATTR(temp##index##_min, S_IRUGO | S_IWUSR, \ ++ show_temp_min, set_temp_min, index-1); \ ++static SENSOR_DEVICE_ATTR(temp##index##_max, S_IRUGO | S_IWUSR, \ ++ show_temp_max, set_temp_max, index-1); \ ++static SENSOR_DEVICE_ATTR(temp##index##_offset, S_IRUGO | S_IWUSR, \ ++ show_temp_offset, set_temp_offset, index-1); ++ ++show_temp_index(1) ++show_temp_index(2) ++show_temp_index(3) ++ ++/* VID */ ++static ssize_t show_vid(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); ++} ++static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); ++ ++static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%d\n", data->vrm); ++} ++ ++static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ data->vrm = simple_strtoul(buf, NULL, 10); ++ return count; ++} ++static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); ++ ++/* Alarms */ ++static ssize_t show_alarm(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 smsc47m192_data *data = smsc47m192_update_device(dev); ++ return sprintf(buf, "%u\n", (data->alarms & nr) ? 1 : 0); ++} ++ ++static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0x0010); ++static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 0x0020); ++static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 0x0040); ++static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 0x4000); ++static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 0x8000); ++static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0x0001); ++static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 0x0002); ++static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 0x0004); ++static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 0x0008); ++static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 0x0100); ++static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200); ++static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400); ++static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800); ++ ++/* This function is called when: ++ * smsc47m192_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and smsc47m192_driver is still present) */ ++static int smsc47m192_attach_adapter(struct i2c_adapter *adapter) ++{ ++ if (!(adapter->class & I2C_CLASS_HWMON)) ++ return 0; ++ return i2c_probe(adapter, &addr_data, smsc47m192_detect); ++} ++ ++static void smsc47m192_init_client(struct i2c_client *client) ++{ ++ int i; ++ u8 config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); ++ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); ++ ++ /* select cycle mode (pause 1 sec between updates) */ ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR, ++ (sfr & 0xfd) | 0x02); ++ if (!(config & 0x01)) { ++ /* initialize alarm limits */ ++ for (i=0; i<8; i++) { ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_IN_MIN(i), 0); ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_IN_MAX(i), 0xff); ++ } ++ for (i=0; i<3; i++) { ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_TEMP_MIN[i], 0x80); ++ i2c_smbus_write_byte_data(client, ++ SMSC47M192_REG_TEMP_MAX[i], 0x7f); ++ } ++ ++ /* start monitoring */ ++ i2c_smbus_write_byte_data(client, SMSC47M192_REG_CONFIG, ++ (config & 0xf7) | 0x01); ++ } ++} ++ ++/* This function is called by i2c_probe */ ++static int smsc47m192_detect(struct i2c_adapter *adapter, int address, ++ int kind) ++{ ++ struct i2c_client *client; ++ struct smsc47m192_data *data; ++ int err = 0; ++ int version, config; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto exit; ++ ++ if (!(data = kzalloc(sizeof(struct smsc47m192_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ client = &data->client; ++ i2c_set_clientdata(client, data); ++ client->addr = address; ++ client->adapter = adapter; ++ client->driver = &smsc47m192_driver; ++ ++ if (kind == 0) ++ kind = smsc47m192; ++ ++ /* Detection criteria from sensors_detect script */ ++ if (kind < 0) { ++ if (i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_COMPANY_ID) == 0x55 ++ && ((version = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_VERSION)) & 0xf0) == 0x20 ++ && (i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_VID) & 0x70) == 0x00 ++ && (i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_VID4) & 0xfe) == 0x80) { ++ dev_info(&adapter->dev, ++ "found SMSC47M192 or SMSC47M997, " ++ "version 2, stepping A%d\n", version & 0x0f); ++ } else { ++ dev_dbg(&adapter->dev, ++ "SMSC47M192 detection failed at 0x%02x\n", ++ address); ++ goto exit_free; ++ } ++ } ++ ++ /* Fill in the remaining client fields and put into the global list */ ++ strlcpy(client->name, "smsc47m192", I2C_NAME_SIZE); ++ data->vrm = vid_which_vrm(); ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(client))) ++ goto exit_free; ++ ++ /* Initialize the SMSC47M192 chip */ ++ smsc47m192_init_client(client); ++ ++ /* Register sysfs hooks */ ++ data->class_dev = hwmon_device_register(&client->dev); ++ if (IS_ERR(data->class_dev)) { ++ err = PTR_ERR(data->class_dev); ++ goto exit_detach; ++ } ++ ++ device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr); ++ ++ /* Pin 110 is either in4 (+12V) or VID4 */ ++ config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); ++ if (!(config & 0x20)) { ++ device_create_file(&client->dev, ++ &sensor_dev_attr_in4_input.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_in4_min.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_in4_max.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_in4_alarm.dev_attr); ++ } ++ device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_temp1_offset.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_temp2_offset.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_temp2_input_fault.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_temp3_offset.dev_attr); ++ device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr); ++ device_create_file(&client->dev, ++ &sensor_dev_attr_temp3_input_fault.dev_attr); ++ device_create_file(&client->dev, &dev_attr_cpu0_vid); ++ device_create_file(&client->dev, &dev_attr_vrm); ++ ++ return 0; ++ ++exit_detach: ++ i2c_detach_client(client); ++exit_free: ++ kfree(data); ++exit: ++ return err; ++} ++ ++static int smsc47m192_detach_client(struct i2c_client *client) ++{ ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ int err; ++ ++ hwmon_device_unregister(data->class_dev); ++ ++ if ((err = i2c_detach_client(client))) ++ return err; ++ ++ kfree(data); ++ ++ return 0; ++} ++ ++static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct smsc47m192_data *data = i2c_get_clientdata(client); ++ int i, config; ++ ++ down(&data->update_lock); ++ ++ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) ++ || !data->valid) { ++ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); ++ ++ dev_dbg(&client->dev, "Starting smsc47m192 update\n"); ++ ++ for (i = 0; i <= 7; i++) { ++ data->in[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_IN(i)); ++ data->in_min[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_IN_MIN(i)); ++ data->in_max[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_IN_MAX(i)); ++ } ++ for (i = 0; i < 3; i++) { ++ data->temp[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_TEMP[i]); ++ data->temp_max[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_TEMP_MAX[i]); ++ data->temp_min[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_TEMP_MIN[i]); ++ } ++ for (i = 1; i < 3; i++) ++ data->temp_offset[i] = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_TEMP_OFFSET(i)); ++ /* first offset is temp_offset[0] if SFR bit 4 is set, ++ temp_offset[1] otherwise */ ++ if (sfr & 0x10) { ++ data->temp_offset[0] = data->temp_offset[1]; ++ data->temp_offset[1] = 0; ++ } else ++ data->temp_offset[0] = 0; ++ ++ data->vid = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID) ++ & 0x0f; ++ config = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_CONFIG); ++ if (config & 0x20) ++ data->vid |= (i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_VID4) & 0x01) << 4; ++ data->alarms = i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_ALARM1) | ++ (i2c_smbus_read_byte_data(client, ++ SMSC47M192_REG_ALARM2) << 8); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++ ++ return data; ++} ++ ++static int __init smsc47m192_init(void) ++{ ++ return i2c_add_driver(&smsc47m192_driver); ++} ++ ++static void __exit smsc47m192_exit(void) ++{ ++ i2c_del_driver(&smsc47m192_driver); ++} ++ ++MODULE_AUTHOR("Hartmut Rick <linux@rick.claranet.de>"); ++MODULE_DESCRIPTION("SMSC47M192 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(smsc47m192_init); ++module_exit(smsc47m192_exit); diff --git a/i2c/hwmon-sysfs-interface-individual-alarm-files.patch b/i2c/hwmon-sysfs-interface-individual-alarm-files.patch new file mode 100644 index 0000000000000..c54aabc2aa1f4 --- /dev/null +++ b/i2c/hwmon-sysfs-interface-individual-alarm-files.patch @@ -0,0 +1,145 @@ +From khali@linux-fr.org Thu Mar 23 07:46:13 2006 +Date: Thu, 23 Mar 2006 16:46:47 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: [PATCH 07/10] hwmon: Add sysfs interface for individual alarm files +Message-Id: <20060323164647.0e89adcb.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-sysfs-interface-individual-alarm-files.patch + +Extend the sysfs interface of hardware monitoring chips, by adding +individual alarm and beep files. Contrary to the old aggregated "alarms" +and "beeps" files, individual files constitute a standard way to access +the status information, making it finally possible to implement a +chip-independant hardware monitoring chip access library (once all +drivers have been added this new interface, that is.) + +If future drivers need more individual files, the interface will be +extended as needed at the same time these drivers are merged into the +kernel tree. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/sysfs-interface | 98 ++++++++++++++++++++++++++---------- + 1 file changed, 73 insertions(+), 25 deletions(-) + +--- gregkh-2.6.orig/Documentation/hwmon/sysfs-interface ++++ gregkh-2.6/Documentation/hwmon/sysfs-interface +@@ -252,9 +252,68 @@ curr[1-n]_input Current input value + Read only. + + +-********* +-* Other * +-********* ++********** ++* Alarms * ++********** ++ ++Each channel or limit may have an associated alarm file, containing a ++boolean value. 1 means than an alarm condition exists, 0 means no alarm. ++ ++Usually a given chip will either use channel-related alarms, or ++limit-related alarms, not both. The driver should just reflect the hardware ++implementation. ++ ++in[0-n]_alarm ++fan[1-n]_alarm ++temp[1-n]_alarm ++ Channel alarm ++ Boolean ++ Read-only ++ ++OR ++ ++in[0-n]_min_alarm ++in[0-n]_max_alarm ++fan[1-n]_min_alarm ++temp[1-n]_min_alarm ++temp[1-n]_max_alarm ++temp[1-n]_crit_alarm ++ Limit alarm ++ Boolean ++ Read-only ++ ++Each input channel may have an associated fault file. This can be used ++to notify open diodes, unconnected fans etc. where the hardware ++supports it. When this boolean has value 1, the measurement for that ++channel should not be trusted. ++ ++in[0-n]_input_fault ++fan[1-n]_input_fault ++temp[1-n]_input_fault ++ Input fault condition ++ Boolean ++ Read-only ++ ++Some chips also offer the possibility to get beeped when an alarm occurs: ++ ++beep_enable Master beep enable ++ Boolean ++ Read/Write ++ ++in[0-n]_beep ++fan[1-n]_beep ++temp[1-n]_beep ++ Channel beep ++ 0 to disable. ++ 1 to enable. ++ Read/write ++ ++In theory, a chip could provide per-limit beep masking, but no such chip ++was seen so far. ++ ++Old drivers provided a different, non-standard interface to alarms and ++beeps. These interface files are deprecated, but will be kept around ++for compatibility reasons: + + alarms Alarm bitmask. + Read only. +@@ -265,33 +324,22 @@ alarms Alarm bitmask. + if it is still valid. + Generally a direct representation of a chip's internal + alarm registers; there is no standard for the position +- of individual bits. ++ of individual bits. For this reason, the use of this ++ interface file for new drivers is discouraged. Use ++ individual *_alarm and *_fault files instead. + Bits are defined in kernel/include/sensors.h. + +-alarms_in Alarm bitmask relative to in (voltage) channels +- Read only +- A '1' bit means an alarm, LSB corresponds to in0 and so on +- Prefered to 'alarms' for newer chips +- +-alarms_fan Alarm bitmask relative to fan channels +- Read only +- A '1' bit means an alarm, LSB corresponds to fan1 and so on +- Prefered to 'alarms' for newer chips +- +-alarms_temp Alarm bitmask relative to temp (temperature) channels +- Read only +- A '1' bit means an alarm, LSB corresponds to temp1 and so on +- Prefered to 'alarms' for newer chips +- +-beep_enable Beep/interrupt enable +- 0 to disable. +- 1 to enable. +- Read/Write +- + beep_mask Bitmask for beep. +- Same format as 'alarms' with the same bit locations. ++ Same format as 'alarms' with the same bit locations, ++ use discouraged for the same reason. Use individual ++ *_beep files instead. + Read/Write + ++ ++********* ++* Other * ++********* ++ + eeprom Raw EEPROM data in binary form. + Read only. + diff --git a/i2c/hwmon-w83627ehf-add-alarms.patch b/i2c/hwmon-w83627ehf-add-alarms.patch new file mode 100644 index 0000000000000..fc3def3473e02 --- /dev/null +++ b/i2c/hwmon-w83627ehf-add-alarms.patch @@ -0,0 +1,132 @@ +From khali@linux-fr.org Thu Mar 23 07:29:53 2006 +Date: Thu, 23 Mar 2006 16:30:29 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Yuan Mu <Ymu@winbond.com.tw>, Rudolf Marek <r.marek@sh.cvut.cz> +Subject: [PATCH 03/10] w83627ehf: Add alarms support +Message-Id: <20060323163029.5d56d96d.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-w83627ehf-add-alarms.patch + +Add alarms support for the W83627EHF/EHG hardware monitoring chip. + +This is based on an earlier patch from Rudolf Marek. + +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/w83627ehf.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 46 insertions(+) + +--- gregkh-2.6.orig/drivers/hwmon/w83627ehf.c ++++ gregkh-2.6/drivers/hwmon/w83627ehf.c +@@ -141,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONF + #define W83627EHF_REG_DIODE 0x59 + #define W83627EHF_REG_SMI_OVT 0x4C + ++#define W83627EHF_REG_ALARM1 0x459 ++#define W83627EHF_REG_ALARM2 0x45A ++#define W83627EHF_REG_ALARM3 0x45B ++ + /* + * Conversions + */ +@@ -218,6 +222,7 @@ struct w83627ehf_data { + s16 temp[2]; + s16 temp_max[2]; + s16 temp_max_hyst[2]; ++ u32 alarms; + }; + + static inline int is_word_sized(u16 reg) +@@ -427,6 +432,13 @@ static struct w83627ehf_data *w83627ehf_ + W83627EHF_REG_TEMP_HYST[i]); + } + ++ data->alarms = w83627ehf_read_value(client, ++ W83627EHF_REG_ALARM1) | ++ (w83627ehf_read_value(client, ++ W83627EHF_REG_ALARM2) << 8) | ++ (w83627ehf_read_value(client, ++ W83627EHF_REG_ALARM3) << 16); ++ + data->last_updated = jiffies; + data->valid = 1; + } +@@ -474,6 +486,14 @@ store_in_##reg (struct device *dev, stru + store_in_reg(MIN, min) + store_in_reg(MAX, max) + ++static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct w83627ehf_data *data = w83627ehf_update_device(dev); ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int nr = sensor_attr->index; ++ return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01); ++} ++ + 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), +@@ -487,6 +507,19 @@ static struct sensor_device_attribute sd + SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9), + }; + ++static struct sensor_device_attribute sda_in_alarm[] = { ++ SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0), ++ SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1), ++ SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2), ++ SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3), ++ SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8), ++ SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21), ++ SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20), ++ SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16), ++ SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17), ++ SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19), ++}; ++ + 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), +@@ -516,6 +549,7 @@ static struct sensor_device_attribute sd + static void device_create_file_in(struct device *dev, int i) + { + device_create_file(dev, &sda_in_input[i].dev_attr); ++ device_create_file(dev, &sda_in_alarm[i].dev_attr); + device_create_file(dev, &sda_in_min[i].dev_attr); + device_create_file(dev, &sda_in_max[i].dev_attr); + } +@@ -618,6 +652,14 @@ static struct sensor_device_attribute sd + SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), + }; + ++static struct sensor_device_attribute sda_fan_alarm[] = { ++ SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6), ++ SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7), ++ SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11), ++ SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10), ++ SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23), ++}; ++ + static struct sensor_device_attribute sda_fan_min[] = { + SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 0), +@@ -642,6 +684,7 @@ static struct sensor_device_attribute sd + static void device_create_file_fan(struct device *dev, int i) + { + device_create_file(dev, &sda_fan_input[i].dev_attr); ++ device_create_file(dev, &sda_fan_alarm[i].dev_attr); + device_create_file(dev, &sda_fan_div[i].dev_attr); + device_create_file(dev, &sda_fan_min[i].dev_attr); + } +@@ -729,6 +772,9 @@ static struct sensor_device_attribute sd + store_temp_max_hyst, 0), + SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 1), ++ SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), ++ SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), ++ SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), + }; + + /* diff --git a/i2c/hwmon-w83627ehf-add-voltages.patch b/i2c/hwmon-w83627ehf-add-voltages.patch new file mode 100644 index 0000000000000..adce43ee714fb --- /dev/null +++ b/i2c/hwmon-w83627ehf-add-voltages.patch @@ -0,0 +1,198 @@ +From khali@linux-fr.org Thu Mar 23 07:24:51 2006 +Date: Thu, 23 Mar 2006 16:25:22 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Yuan Mu <Ymu@winbond.com.tw>, Rudolf Marek <r.marek@sh.cvut.cz> +Subject: [PATCH 02/10] w83627ehf: Add voltage inputs support +Message-Id: <20060323162522.f46f7072.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-w83627ehf-add-voltages.patch + +From: Rudolf Marek <r.marek@sh.cvut.cz> + +Add the voltage measuring support to W83627EHF. The code is based +on the patch provided by Yuan Mu from Winbond. + +Signed-off-by: Yuan Mu <Ymu@winbond.com.tw> +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/w83627ehf.c | 124 ++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 120 insertions(+), 4 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/w83627ehf.c ++++ gregkh-2.6/drivers/hwmon/w83627ehf.c +@@ -30,10 +30,7 @@ + Supports the following chips: + + Chip #vin #fan #pwm #temp chip_id man_id +- w83627ehf - 5 - 3 0x88 0x5ca3 +- +- This is a preliminary version of the driver, only supporting the +- fan and temperature inputs. The chip does much more than that. ++ w83627ehf 10 5 - 3 0x88 0x5ca3 + */ + + #include <linux/module.h> +@@ -121,6 +118,14 @@ superio_exit(void) + static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; + static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; + ++/* The W83627EHF registers for nr=7,8,9 are in bank 5 */ ++#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ ++ (0x554 + (((nr) - 7) * 2))) ++#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ ++ (0x555 + (((nr) - 7) * 2))) ++#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ ++ (0x550 + (nr) - 7)) ++ + #define W83627EHF_REG_TEMP1 0x27 + #define W83627EHF_REG_TEMP1_HYST 0x3a + #define W83627EHF_REG_TEMP1_OVER 0x39 +@@ -172,6 +177,20 @@ temp1_to_reg(int temp) + return (temp + 500) / 1000; + } + ++/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ ++ ++static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; ++ ++static inline long in_from_reg(u8 reg, u8 nr) ++{ ++ return reg * scale_in[nr]; ++} ++ ++static inline u8 in_to_reg(u32 val, u8 nr) ++{ ++ return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255); ++} ++ + /* + * Data structures and manipulation thereof + */ +@@ -186,6 +205,9 @@ struct w83627ehf_data { + unsigned long last_updated; /* In jiffies */ + + /* Register values */ ++ u8 in[10]; /* Register value */ ++ u8 in_max[10]; /* Register value */ ++ u8 in_min[10]; /* Register value */ + u8 fan[5]; + u8 fan_min[5]; + u8 fan_div[5]; +@@ -349,6 +371,16 @@ static struct w83627ehf_data *w83627ehf_ + data->fan_div[3] |= (i >> 5) & 0x04; + } + ++ /* Measured voltages and limits */ ++ for (i = 0; i < 10; i++) { ++ data->in[i] = w83627ehf_read_value(client, ++ W83627EHF_REG_IN(i)); ++ data->in_min[i] = w83627ehf_read_value(client, ++ W83627EHF_REG_IN_MIN(i)); ++ data->in_max[i] = w83627ehf_read_value(client, ++ W83627EHF_REG_IN_MAX(i)); ++ } ++ + /* Measured fan speeds and limits */ + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) +@@ -406,6 +438,87 @@ static struct w83627ehf_data *w83627ehf_ + /* + * Sysfs callback functions + */ ++#define show_in_reg(reg) \ ++static ssize_t \ ++show_##reg(struct device *dev, struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ ++ int nr = sensor_attr->index; \ ++ return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], 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 i2c_client *client = to_i2c_client(dev); \ ++ struct w83627ehf_data *data = i2c_get_clientdata(client); \ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ ++ int nr = sensor_attr->index; \ ++ u32 val = simple_strtoul(buf, NULL, 10); \ ++ \ ++ mutex_lock(&data->update_lock); \ ++ data->in_##reg[nr] = in_to_reg(val, nr); \ ++ w83627ehf_write_value(client, W83627EHF_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), ++}; ++ ++static void device_create_file_in(struct device *dev, int 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); ++} + + #define show_fan_reg(reg) \ + static ssize_t \ +@@ -705,6 +818,9 @@ static int w83627ehf_detect(struct i2c_a + goto exit_detach; + } + ++ for (i = 0; i < 10; i++) ++ device_create_file_in(dev, i); ++ + for (i = 0; i < 5; i++) { + if (data->has_fan & (1 << i)) + device_create_file_fan(dev, i); diff --git a/i2c/hwmon-w83792d-quiet-on-misdetection.patch b/i2c/hwmon-w83792d-quiet-on-misdetection.patch new file mode 100644 index 0000000000000..2fd1a9551ad72 --- /dev/null +++ b/i2c/hwmon-w83792d-quiet-on-misdetection.patch @@ -0,0 +1,48 @@ +From khali@linux-fr.org Thu Mar 23 07:37:51 2006 +Date: Thu, 23 Mar 2006 16:38:21 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: [PATCH 05/10] w83792d: Be quiet on misdetection +Message-Id: <20060323163821.074148b4.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-w83792d-quiet-on-misdetection.patch + +Make the w83792d driver keep quiet when misdetecting a chip. This can +happen, and the user doesn't need to know. + +Also renumber the messages, and add one, for consistency. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/w83792d.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/w83792d.c ++++ gregkh-2.6/drivers/hwmon/w83792d.c +@@ -1161,7 +1161,7 @@ w83792d_detect(struct i2c_adapter *adapt + bank. */ + if (kind < 0) { + if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) { +- dev_warn(dev, "Detection failed at step 3\n"); ++ dev_dbg(dev, "Detection failed at step 1\n"); + goto ERROR1; + } + val1 = w83792d_read_value(client, W83792D_REG_BANK); +@@ -1170,6 +1170,7 @@ w83792d_detect(struct i2c_adapter *adapt + if (!(val1 & 0x07)) { /* is Bank0 */ + if (((!(val1 & 0x80)) && (val2 != 0xa3)) || + ((val1 & 0x80) && (val2 != 0x5c))) { ++ dev_dbg(dev, "Detection failed at step 2\n"); + goto ERROR1; + } + } +@@ -1177,7 +1178,7 @@ w83792d_detect(struct i2c_adapter *adapt + should match */ + if (w83792d_read_value(client, + W83792D_REG_I2C_ADDR) != address) { +- dev_warn(dev, "Detection failed at step 5\n"); ++ dev_dbg(dev, "Detection failed at step 3\n"); + goto ERROR1; + } + } diff --git a/i2c/i2c-parport-require-type-parameter.patch b/i2c/i2c-parport-require-type-parameter.patch new file mode 100644 index 0000000000000..374ae32d23981 --- /dev/null +++ b/i2c/i2c-parport-require-type-parameter.patch @@ -0,0 +1,127 @@ +From khali@linux-fr.org Thu Mar 23 07:49:59 2006 +Date: Thu, 23 Mar 2006 16:50:25 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: "Mark M. Hoffman" <mhoffman@lightlink.com> +Subject: [PATCH 10/10] i2c-parport: Make type parameter mandatory +Message-Id: <20060323165025.ddb0f7c7.khali@linux-fr.org> +Content-Disposition: inline; filename=i2c-parport-require-type-parameter.patch + +From: "Mark M. Hoffman" <mhoffman@lightlink.com> + +This patch forces the user to specify what type of adapter is present when +loading i2c-parport or i2c-parport-light. If none is specified, the driver +init simply fails - instead of assuming adapter type 0. + +This alleviates the sometimes lengthy boot time delays which can be caused +by accidentally building one of these into a kernel along with several i2c +slave drivers that have lengthy probe routines (e.g. hwmon drivers). + +Kconfig and documentation updated accordingly. + +Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/i2c/busses/i2c-parport | 16 ++++++++++------ + drivers/i2c/busses/Kconfig | 5 ++++- + drivers/i2c/busses/i2c-parport-light.c | 9 +++++++-- + drivers/i2c/busses/i2c-parport.c | 9 +++++++-- + drivers/i2c/busses/i2c-parport.h | 2 +- + 5 files changed, 29 insertions(+), 12 deletions(-) + +--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-parport ++++ gregkh-2.6/Documentation/i2c/busses/i2c-parport +@@ -12,18 +12,22 @@ meant as a replacement for the older, in + teletext adapters) + + It currently supports the following devices: +- * Philips adapter +- * home brew teletext adapter +- * Velleman K8000 adapter +- * ELV adapter +- * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032) +- * Barco LPT->DVI (K5800236) adapter ++ * (type=0) Philips adapter ++ * (type=1) home brew teletext adapter ++ * (type=2) Velleman K8000 adapter ++ * (type=3) ELV adapter ++ * (type=4) Analog Devices ADM1032 evaluation board ++ * (type=5) Analog Devices evaluation boards: ADM1025, ADM1030, ADM1031 ++ * (type=6) Barco LPT->DVI (K5800236) adapter + + These devices use different pinout configurations, so you have to tell + the driver what you have, using the type module parameter. There is no + way to autodetect the devices. Support for different pinout configurations + can be easily added when needed. + ++Earlier kernels defaulted to type=0 (Philips). But now, if the type ++parameter is missing, the driver will simply fail to initialize. ++ + + Building your own adapter + ------------------------- +--- gregkh-2.6.orig/drivers/i2c/busses/Kconfig ++++ gregkh-2.6/drivers/i2c/busses/Kconfig +@@ -286,7 +286,10 @@ config I2C_PARPORT + This driver is a replacement for (and was inspired by) an older + driver named i2c-philips-par. The new driver supports more devices, + and makes it easier to add support for new devices. +- ++ ++ An adapter type parameter is now mandatory. Please read the file ++ Documentation/i2c/busses/i2c-parport for details. ++ + Another driver exists, named i2c-parport-light, which doesn't depend + on the parport driver. This is meant for embedded systems. Don't say + Y here if you intend to say Y or M there. +--- gregkh-2.6.orig/drivers/i2c/busses/i2c-parport-light.c ++++ gregkh-2.6/drivers/i2c/busses/i2c-parport-light.c +@@ -121,9 +121,14 @@ static struct i2c_adapter parport_adapte + + static int __init i2c_parport_init(void) + { +- if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) { ++ if (type < 0) { ++ printk(KERN_WARNING "i2c-parport: adapter type unspecified\n"); ++ return -ENODEV; ++ } ++ ++ if (type >= ARRAY_SIZE(adapter_parm)) { + printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); +- type = 0; ++ return -ENODEV; + } + + if (base == 0) { +--- gregkh-2.6.orig/drivers/i2c/busses/i2c-parport.c ++++ gregkh-2.6/drivers/i2c/busses/i2c-parport.c +@@ -241,9 +241,14 @@ static struct parport_driver i2c_parport + + static int __init i2c_parport_init(void) + { +- if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) { ++ if (type < 0) { ++ printk(KERN_WARNING "i2c-parport: adapter type unspecified\n"); ++ return -ENODEV; ++ } ++ ++ if (type >= ARRAY_SIZE(adapter_parm)) { + printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); +- type = 0; ++ return -ENODEV; + } + + return parport_register_driver(&i2c_parport_driver); +--- gregkh-2.6.orig/drivers/i2c/busses/i2c-parport.h ++++ gregkh-2.6/drivers/i2c/busses/i2c-parport.h +@@ -90,7 +90,7 @@ static struct adapter_parm adapter_parm[ + }, + }; + +-static int type; ++static int type = -1; + module_param(type, int, 0); + MODULE_PARM_DESC(type, + "Type of adapter:\n" diff --git a/i2c/i2c-piix4-add-ati-smbus-support.patch b/i2c/i2c-piix4-add-ati-smbus-support.patch new file mode 100644 index 0000000000000..2014fcebf494d --- /dev/null +++ b/i2c/i2c-piix4-add-ati-smbus-support.patch @@ -0,0 +1,86 @@ +From khali@linux-fr.org Thu Mar 23 07:47:43 2006 +Date: Thu, 23 Mar 2006 16:48:09 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Rudolf Marek <r.marek@sh.cvut.cz> +Subject: [PATCH 08/10] i2c-piix4: Add ATI IXP200/300/400 support +Message-Id: <20060323164809.cdcef765.khali@linux-fr.org> +Content-Disposition: inline; filename=i2c-piix4-add-ati-smbus-support.patch + +From: Rudolf Marek <r.marek@sh.cvut.cz> + +This patch adds the ATI IXP southbridges support to i2c-piix4, +as it turned out those chips are compatible with it. + +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 | 2 ++ + drivers/i2c/busses/Kconfig | 5 ++++- + drivers/i2c/busses/i2c-piix4.c | 6 ++++++ + include/linux/pci_ids.h | 3 +++ + 4 files changed, 15 insertions(+), 1 deletion(-) + +--- gregkh-2.6.orig/Documentation/i2c/busses/i2c-piix4 ++++ gregkh-2.6/Documentation/i2c/busses/i2c-piix4 +@@ -6,6 +6,8 @@ Supported adapters: + Datasheet: Publicly available at the Intel website + * ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges + Datasheet: Only available via NDA from ServerWorks ++ * ATI IXP southbridges IXP200, IXP300, IXP400 ++ Datasheet: Not publicly available + * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge + Datasheet: Publicly available at the SMSC website http://www.smsc.com + +--- gregkh-2.6.orig/drivers/i2c/busses/Kconfig ++++ gregkh-2.6/drivers/i2c/busses/Kconfig +@@ -163,7 +163,7 @@ config I2C_PXA_SLAVE + I2C bus. + + config I2C_PIIX4 +- tristate "Intel PIIX4" ++ tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)" + depends on I2C && PCI + help + If you say yes to this option, support will be included for the Intel +@@ -172,6 +172,9 @@ config I2C_PIIX4 + of Broadcom): + Intel PIIX4 + Intel 440MX ++ ATI IXP200 ++ ATI IXP300 ++ ATI IXP400 + Serverworks OSB4 + Serverworks CSB5 + Serverworks CSB6 +--- gregkh-2.6.orig/drivers/i2c/busses/i2c-piix4.c ++++ gregkh-2.6/drivers/i2c/busses/i2c-piix4.c +@@ -413,6 +413,12 @@ static struct i2c_adapter piix4_adapter + static struct pci_device_id piix4_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3), + .driver_data = 3 }, ++ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS), ++ .driver_data = 0 }, ++ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS), ++ .driver_data = 0 }, ++ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS), ++ .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4), + .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5), +--- gregkh-2.6.orig/include/linux/pci_ids.h ++++ gregkh-2.6/include/linux/pci_ids.h +@@ -352,8 +352,11 @@ + #define PCI_DEVICE_ID_ATI_RS480 0x5950 + /* ATI IXP Chipset */ + #define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 ++#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 ++#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 + #define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 + #define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e ++#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 + #define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 + #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 + diff --git a/i2c/i2c-sis96x-remove-init-log-message.patch b/i2c/i2c-sis96x-remove-init-log-message.patch new file mode 100644 index 0000000000000..bab2d8e937871 --- /dev/null +++ b/i2c/i2c-sis96x-remove-init-log-message.patch @@ -0,0 +1,46 @@ +From khali@linux-fr.org Thu Mar 23 07:49:02 2006 +Date: Thu, 23 Mar 2006 16:49:34 +0100 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: "Mark M. Hoffman" <mhoffman@lightlink.com> +Subject: [PATCH 09/10] i2c-sis96x: Remove an init-time log message +Message-Id: <20060323164934.5bc2122f.khali@linux-fr.org> +Content-Disposition: inline; filename=i2c-sis96x-remove-init-log-message.patch + +From: "Mark M. Hoffman" <mhoffman@lightlink.com> + +This patch removes an init-time kernel log message. +http://marc.theaimsgroup.com/?l=linux-kernel&m=114232987208628&w=3 + +Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/i2c/busses/i2c-sis96x.c | 8 -------- + 1 file changed, 8 deletions(-) + +--- gregkh-2.6.orig/drivers/i2c/busses/i2c-sis96x.c ++++ gregkh-2.6/drivers/i2c/busses/i2c-sis96x.c +@@ -43,13 +43,6 @@ + #include <linux/init.h> + #include <asm/io.h> + +-/* +- HISTORY: +- 2003-05-11 1.0.0 Updated from lm_sensors project for kernel 2.5 +- (was i2c-sis645.c from lm_sensors 2.7.0) +-*/ +-#define SIS96x_VERSION "1.0.0" +- + /* base address register in PCI config space */ + #define SIS96x_BAR 0x04 + +@@ -337,7 +330,6 @@ static struct pci_driver sis96x_driver = + + static int __init i2c_sis96x_init(void) + { +- printk(KERN_INFO "i2c-sis96x version %s\n", SIS96x_VERSION); + return pci_register_driver(&sis96x_driver); + } + diff --git a/i2c/rtc-ds1374-convert-tasklet-to-workqueue.patch b/i2c/rtc-ds1374-convert-tasklet-to-workqueue.patch new file mode 100644 index 0000000000000..7af32236b427d --- /dev/null +++ b/i2c/rtc-ds1374-convert-tasklet-to-workqueue.patch @@ -0,0 +1,83 @@ +From khali@linux-fr.org Fri Mar 31 13:04:57 2006 +Date: Fri, 31 Mar 2006 23:05:01 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: [PATCH 1/3] i2c: convert ds1374 to use a workqueue +Message-Id: <20060331230501.c9858518.khali@linux-fr.org> +Content-Disposition: inline; filename=rtc-ds1374-convert-tasklet-to-workqueue.patch + +A tasklet is not suitable for what the ds1374 driver does: neither sleeping +nor mutex operations are allowed in tasklets, and ds1374_set_tlet may do +both. + +We can use a workqueue instead, where both sleeping and mutex operations +are allowed. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Acked-by: Randy Vinson <rvinson@mvista.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/i2c/chips/ds1374.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- gregkh-2.6.orig/drivers/i2c/chips/ds1374.c ++++ gregkh-2.6/drivers/i2c/chips/ds1374.c +@@ -27,6 +27,7 @@ + #include <linux/rtc.h> + #include <linux/bcd.h> + #include <linux/mutex.h> ++#include <linux/workqueue.h> + + #define DS1374_REG_TOD0 0x00 + #define DS1374_REG_TOD1 0x01 +@@ -139,7 +140,7 @@ ulong ds1374_get_rtc_time(void) + return t1; + } + +-static void ds1374_set_tlet(ulong arg) ++static void ds1374_set_work(void *arg) + { + ulong t1, t2; + int limit = 10; /* arbitrary retry limit */ +@@ -168,17 +169,18 @@ static void ds1374_set_tlet(ulong arg) + + static ulong new_time; + +-static DECLARE_TASKLET_DISABLED(ds1374_tasklet, ds1374_set_tlet, +- (ulong) & new_time); ++static struct workqueue_struct *ds1374_workqueue; ++ ++static DECLARE_WORK(ds1374_work, ds1374_set_work, &new_time); + + int ds1374_set_rtc_time(ulong nowtime) + { + new_time = nowtime; + + if (in_interrupt()) +- tasklet_schedule(&ds1374_tasklet); ++ queue_work(ds1374_workqueue, &ds1374_work); + else +- ds1374_set_tlet((ulong) & new_time); ++ ds1374_set_work(&new_time); + + return 0; + } +@@ -204,6 +206,8 @@ static int ds1374_probe(struct i2c_adapt + client->adapter = adap; + client->driver = &ds1374_driver; + ++ ds1374_workqueue = create_singlethread_workqueue("ds1374"); ++ + if ((rc = i2c_attach_client(client)) != 0) { + kfree(client); + return rc; +@@ -227,7 +231,7 @@ static int ds1374_detach(struct i2c_clie + + if ((rc = i2c_detach_client(client)) == 0) { + kfree(i2c_get_clientdata(client)); +- tasklet_kill(&ds1374_tasklet); ++ destroy_workqueue(ds1374_workqueue); + } + return rc; + } diff --git a/i2c/rtc-m41t00-driver-cleanup.patch b/i2c/rtc-m41t00-driver-cleanup.patch new file mode 100644 index 0000000000000..33b19dc070377 --- /dev/null +++ b/i2c/rtc-m41t00-driver-cleanup.patch @@ -0,0 +1,145 @@ +From khali@linux-fr.org Fri Mar 31 13:06:41 2006 +Date: Fri, 31 Mar 2006 23:06:46 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: "Mark A. Greer" <mgreer@mvista.com> +Subject: [PATCH 3/3] i2c: cleanup m41t00 +Message-Id: <20060331230646.e06f73a1.khali@linux-fr.org> +Content-Disposition: inline; filename=rtc-m41t00-driver-cleanup.patch + +From: "Mark A. Greer" <mgreer@mvista.com> + +This patch does some cleanup to the m41t00 i2c/rtc driver including: +- use BCD2BIN/BIN2BCD instead of BCD_TO_BIN/BIN_TO_BCD +- use strlcpy instead of strncpy +- some whitespace cleanup + +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 | 49 +++++++++++++++++++-------------------------- + 1 file changed, 21 insertions(+), 28 deletions(-) + +--- gregkh-2.6.orig/drivers/i2c/chips/m41t00.c ++++ gregkh-2.6/drivers/i2c/chips/m41t00.c +@@ -1,6 +1,4 @@ + /* +- * drivers/i2c/chips/m41t00.c +- * + * I2C client/driver for the ST M41T00 Real-Time Clock chip. + * + * Author: Mark A. Greer <mgreer@mvista.com> +@@ -13,9 +11,6 @@ + /* + * This i2c client/driver wedges between the drivers/char/genrtc.c RTC + * interface and the SMBus interface of the i2c subsystem. +- * It would be more efficient to use i2c msgs/i2c_transfer directly but, as +- * recommened in .../Documentation/i2c/writing-clients section +- * "Sending and receiving", using SMBus level communication is preferred. + */ + + #include <linux/kernel.h> +@@ -41,17 +36,17 @@ static unsigned short ignore[] = { I2C_C + static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END }; + + static struct i2c_client_address_data addr_data = { +- .normal_i2c = normal_addr, +- .probe = ignore, +- .ignore = ignore, ++ .normal_i2c = normal_addr, ++ .probe = ignore, ++ .ignore = ignore, + }; + + ulong + m41t00_get_rtc_time(void) + { +- s32 sec, min, hour, day, mon, year; +- s32 sec1, min1, hour1, day1, mon1, year1; +- ulong limit = 10; ++ s32 sec, min, hour, day, mon, year; ++ s32 sec1, min1, hour1, day1, mon1, year1; ++ ulong limit = 10; + + sec = min = hour = day = mon = year = 0; + sec1 = min1 = hour1 = day1 = mon1 = year1 = 0; +@@ -97,12 +92,12 @@ m41t00_get_rtc_time(void) + mon &= 0x1f; + year &= 0xff; + +- BCD_TO_BIN(sec); +- BCD_TO_BIN(min); +- BCD_TO_BIN(hour); +- BCD_TO_BIN(day); +- BCD_TO_BIN(mon); +- BCD_TO_BIN(year); ++ sec = BCD2BIN(sec); ++ min = BCD2BIN(min); ++ hour = BCD2BIN(hour); ++ day = BCD2BIN(day); ++ mon = BCD2BIN(mon); ++ year = BCD2BIN(year); + + year += 1900; + if (year < 1970) +@@ -115,17 +110,17 @@ static void + m41t00_set(void *arg) + { + struct rtc_time tm; +- ulong nowtime = *(ulong *)arg; ++ ulong nowtime = *(ulong *)arg; + + to_tm(nowtime, &tm); + tm.tm_year = (tm.tm_year - 1900) % 100; + +- BIN_TO_BCD(tm.tm_sec); +- BIN_TO_BCD(tm.tm_min); +- BIN_TO_BCD(tm.tm_hour); +- BIN_TO_BCD(tm.tm_mon); +- BIN_TO_BCD(tm.tm_mday); +- BIN_TO_BCD(tm.tm_year); ++ 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) +@@ -143,7 +138,6 @@ m41t00_set(void *arg) + dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n"); + + mutex_unlock(&m41t00_mutex); +- return; + } + + static ulong new_time; +@@ -180,7 +174,7 @@ m41t00_probe(struct i2c_adapter *adap, i + if (!client) + return -ENOMEM; + +- strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); ++ strlcpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); + client->addr = addr; + client->adapter = adap; + client->driver = &m41t00_driver; +@@ -204,7 +198,7 @@ m41t00_attach(struct i2c_adapter *adap) + static int + m41t00_detach(struct i2c_client *client) + { +- int rc; ++ int rc; + + if ((rc = i2c_detach_client(client)) == 0) { + kfree(client); +@@ -232,7 +226,6 @@ static void __exit + m41t00_exit(void) + { + i2c_del_driver(&m41t00_driver); +- return; + } + + module_init(m41t00_init); diff --git a/i2c/rtc-m41t00-driver-should-use-workqueue-instead-of-tasklet.patch b/i2c/rtc-m41t00-driver-should-use-workqueue-instead-of-tasklet.patch new file mode 100644 index 0000000000000..1ff11eedc31de --- /dev/null +++ b/i2c/rtc-m41t00-driver-should-use-workqueue-instead-of-tasklet.patch @@ -0,0 +1,84 @@ +From khali@linux-fr.org Fri Mar 31 13:05:53 2006 +Date: Fri, 31 Mar 2006 23:06:03 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: "Mark A. Greer" <mgreer@mvista.com> +Subject: [PATCH 2/3] i2c: convert m41t00 to use a workqueue +Message-Id: <20060331230603.4a8bc540.khali@linux-fr.org> +Content-Disposition: inline; filename=rtc-m41t00-driver-should-use-workqueue-instead-of-tasklet.patch + +From: "Mark A. Greer" <mgreer@mvista.com> + +The m41t00 i2c/rtc driver currently uses a tasklet to schedule +interrupt-level writes to the rtc. This patch causes the driver +to use a workqueue instead. + +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 | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +--- gregkh-2.6.orig/drivers/i2c/chips/m41t00.c ++++ gregkh-2.6/drivers/i2c/chips/m41t00.c +@@ -25,6 +25,7 @@ + #include <linux/rtc.h> + #include <linux/bcd.h> + #include <linux/mutex.h> ++#include <linux/workqueue.h> + + #include <asm/time.h> + #include <asm/rtc.h> +@@ -111,7 +112,7 @@ m41t00_get_rtc_time(void) + } + + static void +-m41t00_set_tlet(ulong arg) ++m41t00_set(void *arg) + { + struct rtc_time tm; + ulong nowtime = *(ulong *)arg; +@@ -145,9 +146,9 @@ m41t00_set_tlet(ulong arg) + return; + } + +-static ulong new_time; +- +-DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time); ++static ulong new_time; ++static struct workqueue_struct *m41t00_wq; ++static DECLARE_WORK(m41t00_work, m41t00_set, &new_time); + + int + m41t00_set_rtc_time(ulong nowtime) +@@ -155,9 +156,9 @@ m41t00_set_rtc_time(ulong nowtime) + new_time = nowtime; + + if (in_interrupt()) +- tasklet_schedule(&m41t00_tasklet); ++ queue_work(m41t00_wq, &m41t00_work); + else +- m41t00_set_tlet((ulong)&new_time); ++ m41t00_set(&new_time); + + return 0; + } +@@ -189,6 +190,7 @@ m41t00_probe(struct i2c_adapter *adap, i + return rc; + } + ++ m41t00_wq = create_singlethread_workqueue("m41t00"); + save_client = client; + return 0; + } +@@ -206,7 +208,7 @@ m41t00_detach(struct i2c_client *client) + + if ((rc = i2c_detach_client(client)) == 0) { + kfree(client); +- tasklet_kill(&m41t00_tasklet); ++ destroy_workqueue(m41t00_wq); + } + return rc; + } |