From 43cb7ebee2f478d3f987ad773d4e6b07fc23c631 Mon Sep 17 00:00:00 2001
From: Jordan Crouse <jordan.crouse@amd.com>
Date: Thu, 23 Mar 2006 16:19:49 +0100
Subject: [PATCH] lm83: Add LM82 support

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>
---
 drivers/hwmon/Kconfig |  4 ++--
 drivers/hwmon/lm83.c  | 50 +++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 39 insertions(+), 15 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 99cdc61..a6a8d661 100644
--- a/drivers/hwmon/Kconfig
+++ b/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.
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index aac4ec2..2137d78 100644
--- a/drivers/hwmon/lm83.c
+++ b/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[] = { 0x18, 0x19, 0x1a,
  * 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_adapter *adapter, int address, int kind)
 		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_adapter *adapter, int address, int kind)
 
 	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_adapter *adapter, int address, int kind)
 		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:
-- 
cgit v1.1


From cf0676fe42c8e14c4c7ed5895e2fe5d17f779b43 Mon Sep 17 00:00:00 2001
From: Rudolf Marek <r.marek@sh.cvut.cz>
Date: Thu, 23 Mar 2006 16:25:22 +0100
Subject: [PATCH] w83627ehf: Add voltage inputs support

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(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index b6bd568..08bbeaf 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/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_update_device(struct device *dev)
 			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_update_device(struct device *dev)
 /*
  * 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_adapter *adapter)
 		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);
-- 
cgit v1.1


From a4589dbb4e2dc758d1aadb8b5d7e5a598ccb0341 Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Thu, 23 Mar 2006 16:30:29 +0100
Subject: [PATCH] w83627ehf: Add alarms support

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(+)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 08bbeaf..40301bc 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -141,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
 #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_update_device(struct device *dev)
 						 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, struct device_attribute *attr, \
 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 sda_in_input[] = {
 	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 sda_in_max[] = {
 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 sda_fan_input[] = {
 	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 sda_fan_div[] = {
 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 sda_temp[] = {
 		    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),
 };
 
 /*
-- 
cgit v1.1


From 59ac83677f72ea2cc25b5426e7df9589aa7a5384 Mon Sep 17 00:00:00 2001
From: Hartmut Rick <linux@rick.claranet.de>
Date: Thu, 23 Mar 2006 16:37:23 +0100
Subject: [PATCH] smsc47m192: New hwmon driver for SMSC LPC47M192/997

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>
---
 drivers/hwmon/Kconfig      |  23 +-
 drivers/hwmon/Makefile     |   1 +
 drivers/hwmon/smsc47m192.c | 648 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 671 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwmon/smsc47m192.c

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a6a8d661..9cf3d9c 100644
--- a/drivers/hwmon/Kconfig
+++ b/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
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index fbdb8d9..d090459 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 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
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
new file mode 100644
index 0000000..bdc4570
--- /dev/null
+++ b/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);
-- 
cgit v1.1


From 568825c8ed3c902981ce908ed5693abb3c9ddf59 Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Thu, 23 Mar 2006 16:40:23 +0100
Subject: [PATCH] f71805f: Resource needs not be global

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(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index 885465d..fd72440 100644
--- a/drivers/hwmon/f71805f.c
+++ b/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_driver = {
 
 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(unsigned short address)
 		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);
-- 
cgit v1.1


From 4f507ea1560805b16cf4a4359e304ae0db0c2dcb Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Tue, 25 Apr 2006 14:20:11 +0200
Subject: [PATCH] HWMON: hdaps: Update the list of supported systems

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(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c
index 1659f6c..7841fa0 100644
--- a/drivers/hwmon/hdaps.c
+++ b/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 }
 	};
 
-- 
cgit v1.1


From 9873964d6eb24bd0205394f9b791de9eddbcb855 Mon Sep 17 00:00:00 2001
From: Charles Spirakis <bezaur@gmail.com>
Date: Tue, 25 Apr 2006 14:21:03 +0200
Subject: [PATCH] HWMON: w83791d: New hardware monitoring driver for the
 Winbond W83791D

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>
---
 drivers/hwmon/Kconfig   |   10 +
 drivers/hwmon/Makefile  |    1 +
 drivers/hwmon/w83791d.c | 1255 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1266 insertions(+)
 create mode 100644 drivers/hwmon/w83791d.c

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9cf3d9c..9384b85 100644
--- a/drivers/hwmon/Kconfig
+++ b/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
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d090459..db72b14 100644
--- a/drivers/hwmon/Makefile
+++ b/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
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
new file mode 100644
index 0000000..eec43ab
--- /dev/null
+++ b/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);
-- 
cgit v1.1


From 0d0001dd956d4ae2c8739c2877fa660eec68ed5f Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Tue, 25 Apr 2006 14:23:01 +0200
Subject: [PATCH] HWMON: Improve the help text for CONFIG_HWMON

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>
---
 drivers/hwmon/Kconfig | 4 ++++
 1 file changed, 4 insertions(+)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9384b85..164760d 100644
--- a/drivers/hwmon/Kconfig
+++ b/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.
 
-- 
cgit v1.1


From 177d165d7c2ec424007d6241a93d0cb83d953c51 Mon Sep 17 00:00:00 2001
From: Rudolf Marek <r.marek@sh.cvut.cz>
Date: Tue, 25 Apr 2006 14:24:46 +0200
Subject: [PATCH] HWMON: Trim VID values to correct number of bits

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(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c
index a74a44f..b0fd267 100644
--- a/drivers/hwmon/hwmon-vid.c
+++ b/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;
-- 
cgit v1.1


From 0a704681f37f8cd84fa10312728a39e9fcf2c2c5 Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Sun, 4 Jun 2006 20:10:55 +0200
Subject: [PATCH] hwmon: Fix a typo in the hdaps driver

Fix a typo in the hdaps driver.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Acked-by: Robert Love <rml@novell.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/hdaps.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c
index 7841fa0..42b6328 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/hwmon/hdaps.c
@@ -41,7 +41,7 @@
 #define HDAPS_PORT_STATE	0x1611	/* device state */
 #define HDAPS_PORT_YPOS		0x1612	/* y-axis position */
 #define	HDAPS_PORT_XPOS		0x1614	/* x-axis position */
-#define HDAPS_PORT_TEMP1	0x1616	/* device temperature, in celcius */
+#define HDAPS_PORT_TEMP1	0x1616	/* device temperature, in Celsius */
 #define HDAPS_PORT_YVAR		0x1617	/* y-axis variance (what is this?) */
 #define HDAPS_PORT_XVAR		0x1619	/* x-axis variance (what is this?) */
 #define HDAPS_PORT_TEMP2	0x161b	/* device temperature (again?) */
-- 
cgit v1.1


From 53e2761bb2e4ed58913c266ad13e8c10692aec1e Mon Sep 17 00:00:00 2001
From: Yuan Mu <ymu@winbond.com.tw>
Date: Sun, 4 Jun 2006 20:18:05 +0200
Subject: [PATCH] w83792d: Fix setting the PWM value

W83792D use pwm register low 4 bits to store PWM/DC value, bit 7
is used to store fan PWM/DC mode. The store_pwm function did not
convert the pwm input correctly, so it may change the fan mode
when new value is set.

This fix the problem. Change the "index" value of pwm*_mode
and pwm* SENSOR_ATTR to simplify code.

Signed-off-by: Yuan Mu <ymu@winbond.com.tw>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/w83792d.c | 56 +++++++++++++++++++++++++------------------------
 1 file changed, 29 insertions(+), 27 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 958602e..6cdef18 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div)
 			: (val)) / 1000, 0, 0xff))
 #define TEMP_ADD_TO_REG_LOW(val)	((val%1000) ? 0x80 : 0x00)
 
-#define PWM_FROM_REG(val)		(val)
-#define PWM_TO_REG(val)			(SENSORS_LIMIT((val),0,255))
 #define DIV_FROM_REG(val)		(1 << (val))
 
 static inline u8
@@ -291,7 +289,6 @@ struct w83792d_data {
 	u8 pwm[7];		/* We only consider the first 3 set of pwm,
 				   although 792 chip has 7 set of pwm. */
 	u8 pwmenable[3];
-	u8 pwm_mode[7];		/* indicates PWM or DC mode: 1->PWM; 0->DC */
 	u32 alarms;		/* realtime status register encoding,combined */
 	u8 chassis;		/* Chassis status */
 	u8 chassis_clear;	/* CLR_CHS, clear chassis intrusion detection */
@@ -627,7 +624,7 @@ show_pwm(struct device *dev, struct device_attribute *attr,
 	struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
 	int nr = sensor_attr->index;
 	struct w83792d_data *data = w83792d_update_device(dev);
-	return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr-1]));
+	return sprintf(buf, "%d\n", (data->pwm[nr] & 0x0f) << 4);
 }
 
 static ssize_t
@@ -659,14 +656,16 @@ store_pwm(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 - 1;
+	int nr = sensor_attr->index;
 	struct i2c_client *client = to_i2c_client(dev);
 	struct w83792d_data *data = i2c_get_clientdata(client);
-	u32 val;
+	u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255) >> 4;
 
-	val = simple_strtoul(buf, NULL, 10);
-	data->pwm[nr] = PWM_TO_REG(val);
+	mutex_lock(&data->update_lock);
+	val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0;
+	data->pwm[nr] = val;
 	w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -707,9 +706,9 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
 }
 
 static struct sensor_device_attribute sda_pwm[] = {
-	SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
-	SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
-	SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
+	SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
+	SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
+	SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
 };
 static struct sensor_device_attribute sda_pwm_enable[] = {
 	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
@@ -728,7 +727,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr,
 	struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
 	int nr = sensor_attr->index;
 	struct w83792d_data *data = w83792d_update_device(dev);
-	return sprintf(buf, "%d\n", data->pwm_mode[nr-1]);
+	return sprintf(buf, "%d\n", data->pwm[nr] >> 7);
 }
 
 static ssize_t
@@ -736,29 +735,35 @@ store_pwm_mode(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 - 1;
+	int nr = sensor_attr->index;
 	struct i2c_client *client = to_i2c_client(dev);
 	struct w83792d_data *data = i2c_get_clientdata(client);
 	u32 val;
-	u8 pwm_mode_mask = 0;
 
 	val = simple_strtoul(buf, NULL, 10);
-	data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1);
-	pwm_mode_mask = w83792d_read_value(client,
-		W83792D_REG_PWM[nr]) & 0x7f;
-	w83792d_write_value(client, W83792D_REG_PWM[nr],
-		((data->pwm_mode[nr]) << 7) | pwm_mode_mask);
+	if (val != 0 && val != 1)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+	data->pwm[nr] = w83792d_read_value(client, W83792D_REG_PWM[nr]);
+	if (val) {			/* PWM mode */
+		data->pwm[nr] |= 0x80;
+	} else {			/* DC mode */
+		data->pwm[nr] &= 0x7f;
+	}
+	w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
 
 static struct sensor_device_attribute sda_pwm_mode[] = {
 	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
-		    show_pwm_mode, store_pwm_mode, 1),
+		    show_pwm_mode, store_pwm_mode, 0),
 	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
-		    show_pwm_mode, store_pwm_mode, 2),
+		    show_pwm_mode, store_pwm_mode, 1),
 	SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
-		    show_pwm_mode, store_pwm_mode, 3),
+		    show_pwm_mode, store_pwm_mode, 2),
 };
 
 
@@ -1373,7 +1378,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
 	struct i2c_client *client = to_i2c_client(dev);
 	struct w83792d_data *data = i2c_get_clientdata(client);
 	int i, j;
-	u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp;
+	u8 reg_array_tmp[4], reg_tmp;
 
 	mutex_lock(&data->update_lock);
 
@@ -1402,10 +1407,8 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
 			data->fan_min[i] = w83792d_read_value(client,
 						W83792D_REG_FAN_MIN[i]);
 			/* Update the PWM/DC Value and PWM/DC flag */
-			pwm_array_tmp[i] = w83792d_read_value(client,
+			data->pwm[i] = w83792d_read_value(client,
 						W83792D_REG_PWM[i]);
-			data->pwm[i] = pwm_array_tmp[i] & 0x0f;
-			data->pwm_mode[i] = pwm_array_tmp[i] >> 7;
 		}
 
 		reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG);
@@ -1513,7 +1516,6 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev)
 		dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]);
 		dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
 		dev_dbg(dev, "pwm[%d]     is: 0x%x\n", i, data->pwm[i]);
-		dev_dbg(dev, "pwm_mode[%d] is: 0x%x\n", i, data->pwm_mode[i]);
 	}
 	dev_dbg(dev, "3 set of Temperatures: =====>\n");
 	for (i=0; i<3; i++) {
-- 
cgit v1.1


From bed730821b74be4c7d135098842219473f7c8f2c Mon Sep 17 00:00:00 2001
From: Yuan Mu <ymu@winbond.com.tw>
Date: Sun, 4 Jun 2006 20:18:45 +0200
Subject: [PATCH] w83792d: Add missing data access locks

Add missing data lock in w83792d driver to avoid unexpected
data change.

Signed-off-by: Yuan Mu <ymu@winbond.com.tw>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/w83792d.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 6cdef18..4ef884c 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -372,8 +372,10 @@ static ssize_t store_in_##reg (struct device *dev, \
 	u32 val; \
 	 \
 	val = simple_strtoul(buf, NULL, 10); \
+	mutex_lock(&data->update_lock); \
 	data->in_##reg[nr] = SENSORS_LIMIT(IN_TO_REG(nr, val)/4, 0, 255); \
 	w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \
+	mutex_unlock(&data->update_lock); \
 	 \
 	return count; \
 }
@@ -440,9 +442,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
 	u32 val;
 
 	val = simple_strtoul(buf, NULL, 10);
+	mutex_lock(&data->update_lock);
 	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
 	w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
 				data->fan_min[nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -475,6 +479,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
 	u8 tmp_fan_div;
 
 	/* Save fan_min */
+	mutex_lock(&data->update_lock);
 	min = FAN_FROM_REG(data->fan_min[nr],
 			   DIV_FROM_REG(data->fan_div[nr]));
 
@@ -490,6 +495,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
 	/* Restore fan_min */
 	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
 	w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -544,10 +550,11 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr,
 	s32 val;
 
 	val = simple_strtol(buf, NULL, 10);
-
+	mutex_lock(&data->update_lock);
 	data->temp1[nr] = TEMP1_TO_REG(val);
 	w83792d_write_value(client, W83792D_REG_TEMP1[nr],
 		data->temp1[nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -577,13 +584,14 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
 	s32 val;
 
 	val = simple_strtol(buf, NULL, 10);
-
+	mutex_lock(&data->update_lock);
 	data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val);
 	data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val);
 	w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index],
 		data->temp_add[nr][index]);
 	w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1],
 		data->temp_add[nr][index+1]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -682,6 +690,10 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
 	u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp;
 
 	val = simple_strtoul(buf, NULL, 10);
+	if (val < 1 || val > 3)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
 	switch (val) {
 	case 1:
 		data->pwmenable[nr] = 0; /* manual mode */
@@ -692,8 +704,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
 	case 3:
 		data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */
 		break;
-	default:
-		return -EINVAL;
 	}
 	cfg1_tmp = data->pwmenable[0];
 	cfg2_tmp = (data->pwmenable[1]) << 2;
@@ -701,6 +711,7 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
 	cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0;
 	fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp;
 	w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -794,12 +805,13 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr,
 	u8 temp1 = 0, temp2 = 0;
 
 	val = simple_strtoul(buf, NULL, 10);
-
+	mutex_lock(&data->update_lock);
 	data->chassis_clear = SENSORS_LIMIT(val, 0 ,1);
 	temp1 = ((data->chassis_clear) << 7) & 0x80;
 	temp2 = w83792d_read_value(client,
 		W83792D_REG_CHASSIS_CLR) & 0x7f;
 	w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -832,10 +844,12 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr,
 	val = simple_strtoul(buf, NULL, 10);
 	target_tmp = val;
 	target_tmp = target_tmp & 0x7f;
+	mutex_lock(&data->update_lock);
 	target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80;
 	data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255);
 	w83792d_write_value(client, W83792D_REG_THERMAL[nr],
 		(data->thermal_cruise[nr]) | target_mask);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -872,6 +886,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
 	u8 tol_tmp, tol_mask;
 
 	val = simple_strtoul(buf, NULL, 10);
+	mutex_lock(&data->update_lock);
 	tol_mask = w83792d_read_value(client,
 		W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0);
 	tol_tmp = SENSORS_LIMIT(val, 0, 15);
@@ -882,6 +897,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
 	}
 	w83792d_write_value(client, W83792D_REG_TOLERANCE[nr],
 		tol_mask | tol_tmp);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -920,11 +936,13 @@ store_sf2_point(struct device *dev, struct device_attribute *attr,
 	u8 mask_tmp = 0;
 
 	val = simple_strtoul(buf, NULL, 10);
+	mutex_lock(&data->update_lock);
 	data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127);
 	mask_tmp = w83792d_read_value(client,
 					W83792D_REG_POINTS[index][nr]) & 0x80;
 	w83792d_write_value(client, W83792D_REG_POINTS[index][nr],
 		mask_tmp|data->sf2_points[index][nr]);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
@@ -984,6 +1002,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
 	u8 mask_tmp=0, level_tmp=0;
 
 	val = simple_strtoul(buf, NULL, 10);
+	mutex_lock(&data->update_lock);
 	data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15);
 	mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr])
 		& ((nr==3) ? 0xf0 : 0x0f);
@@ -993,6 +1012,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
 		level_tmp = data->sf2_levels[index][nr] << 4;
 	}
 	w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp);
+	mutex_unlock(&data->update_lock);
 
 	return count;
 }
-- 
cgit v1.1


From f2b84bbcebfdbe4855bab532909eef6621999f9f Mon Sep 17 00:00:00 2001
From: Hans de Goede <j.w.r.degoede@hhs.nl>
Date: Sun, 4 Jun 2006 20:22:24 +0200
Subject: [PATCH] abituguru: New hardware monitoring driver

New hardware monitoring driver for the Abit uGuru

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/Kconfig     |   12 +
 drivers/hwmon/Makefile    |    1 +
 drivers/hwmon/abituguru.c | 1391 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1404 insertions(+)
 create mode 100644 drivers/hwmon/abituguru.c

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 164760d..6fb93d6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -27,6 +27,18 @@ config HWMON_VID
 	tristate
 	default n
 
+config SENSORS_ABITUGURU
+	tristate "Abit uGuru"
+	depends on HWMON && EXPERIMENTAL
+	help
+	  If you say yes here you get support for the Abit uGuru chips
+	  sensor part. The voltage and frequency control parts of the Abit
+	  uGuru are not supported. The Abit uGuru chip can be found on Abit
+	  uGuru featuring motherboards (most modern Abit motherboards).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called abituguru.
+
 config SENSORS_ADM1021
 	tristate "Analog Devices ADM1021 and compatibles"
 	depends on HWMON && I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index db72b14..5092999 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SENSORS_W83792D)	+= w83792d.o
 obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
 obj-$(CONFIG_SENSORS_W83791D)	+= w83791d.o
 
+obj-$(CONFIG_SENSORS_ABITUGURU)	+= abituguru.o
 obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
 obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025.o
 obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
new file mode 100644
index 0000000..bf2cb0a
--- /dev/null
+++ b/drivers/hwmon/abituguru.c
@@ -0,0 +1,1391 @@
+/*
+    abituguru.c Copyright (c) 2005-2006 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+    This driver supports the sensor part of the custom Abit uGuru chip found
+    on Abit uGuru motherboards. Note: because of lack of specs the CPU / RAM /
+    etc voltage & frequency control is not supported!
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <asm/io.h>
+
+/* Banks */
+#define ABIT_UGURU_ALARM_BANK			0x20 /* 1x 3 bytes */
+#define ABIT_UGURU_SENSOR_BANK1			0x21 /* 16x volt and temp */
+#define ABIT_UGURU_FAN_PWM			0x24 /* 3x 5 bytes */
+#define ABIT_UGURU_SENSOR_BANK2			0x26 /* fans */
+/* max nr of sensors in bank2, currently mb's with max 6 fans are known */
+#define ABIT_UGURU_MAX_BANK2_SENSORS		6
+/* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */
+#define ABIT_UGURU_MAX_PWMS			5
+/* uGuru sensor bank 1 flags */			     /* Alarm if: */
+#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE	0x01 /*  temp over warn */
+#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE	0x02 /*  volt over max */
+#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE	0x04 /*  volt under min */
+#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG		0x10 /* temp is over warn */
+#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG		0x20 /* volt is over max */
+#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG		0x40 /* volt is under min */
+/* uGuru sensor bank 2 flags */			     /* Alarm if: */
+#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE		0x01 /*   fan under min */
+/* uGuru sensor bank common flags */
+#define ABIT_UGURU_BEEP_ENABLE			0x08 /* beep if alarm */
+#define ABIT_UGURU_SHUTDOWN_ENABLE		0x80 /* shutdown if alarm */
+/* uGuru fan PWM (speed control) flags */
+#define ABIT_UGURU_FAN_PWM_ENABLE		0x80 /* enable speed control */
+/* Values used for conversion */
+#define ABIT_UGURU_FAN_MAX			15300 /* RPM */
+/* Bank1 sensor types */
+#define ABIT_UGURU_IN_SENSOR			0
+#define ABIT_UGURU_TEMP_SENSOR			1
+#define ABIT_UGURU_NC				2
+/* Timeouts / Retries, if these turn out to need a lot of fiddling we could
+   convert them to params. */
+/* 250 was determined by trial and error, 200 works most of the time, but not
+   always. I assume this is cpu-speed independent, since the ISA-bus and not
+   the CPU should be the bottleneck. Note that 250 sometimes is still not
+   enough (only reported on AN7 mb) this is handled by a higher layer. */
+#define ABIT_UGURU_WAIT_TIMEOUT			250
+/* Normally all expected status in abituguru_ready, are reported after the
+   first read, but sometimes not and we need to poll, 5 polls was not enough
+   50 sofar is. */
+#define ABIT_UGURU_READY_TIMEOUT		50
+/* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */
+#define ABIT_UGURU_MAX_RETRIES			3
+#define ABIT_UGURU_RETRY_DELAY			(HZ/5)
+/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is a error */
+#define ABIT_UGURU_MAX_TIMEOUTS			2
+
+/* All the variables below are named identical to the oguru and oguru2 programs
+   reverse engineered by Olle Sandberg, hence the names might not be 100%
+   logical. I could come up with better names, but I prefer keeping the names
+   identical so that this driver can be compared with his work more easily. */
+/* Two i/o-ports are used by uGuru */
+#define ABIT_UGURU_BASE				0x00E0
+/* Used to tell uGuru what to read and to read the actual data */
+#define ABIT_UGURU_CMD				0x00
+/* Mostly used to check if uGuru is busy */
+#define ABIT_UGURU_DATA				0x04
+#define ABIT_UGURU_REGION_LENGTH		5
+/* uGuru status' */
+#define ABIT_UGURU_STATUS_WRITE			0x00 /* Ready to be written */
+#define ABIT_UGURU_STATUS_READ			0x01 /* Ready to be read */
+#define ABIT_UGURU_STATUS_INPUT			0x08 /* More input */
+#define ABIT_UGURU_STATUS_READY			0x09 /* Ready to be written */
+/* utility macros */
+#define ABIT_UGURU_NAME				"abituguru"
+#define ABIT_UGURU_DEBUG(level, format, arg...)				\
+	if (level <= verbose)						\
+		printk(KERN_DEBUG ABIT_UGURU_NAME ": "	format , ## arg)
+
+/* Constants */
+/* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */
+static const int abituguru_bank1_max_value[2] = { 3494, 255000 };
+/* Min / Max allowed values for sensor2 (fan) alarm threshold, these values
+   correspond to 300-3000 RPM */
+static const u8 abituguru_bank2_min_threshold = 5;
+static const u8 abituguru_bank2_max_threshold = 50;
+/* Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4
+   are temperature trip points. */
+static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 };
+/* Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a
+   special case the minium allowed pwm% setting for this is 30% (77) on
+   some MB's this special case is handled in the code! */
+static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 };
+static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 };
+
+
+/* Insmod parameters */
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Set to one to force detection.");
+static int fan_sensors;
+module_param(fan_sensors, int, 0);
+MODULE_PARM_DESC(fan_sensors, "Number of fan sensors on the uGuru "
+	"(0 = autodetect)");
+static int pwms;
+module_param(pwms, int, 0);
+MODULE_PARM_DESC(pwms, "Number of PWMs on the uGuru "
+	"(0 = autodetect)");
+
+/* Default verbose is 2, since this driver is still in the testing phase */
+static int verbose = 2;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n"
+	"   0 normal output\n"
+	"   1 + verbose error reporting\n"
+	"   2 + sensors type probing info\n"
+	"   3 + retryable error reporting");
+
+
+/* For the Abit uGuru, we need to keep some data in memory.
+   The structure is dynamically allocated, at the same time when a new
+   abituguru device is allocated. */
+struct abituguru_data {
+	struct class_device *class_dev; /* hwmon registered device */
+	struct mutex update_lock;	/* protect access to data and uGuru */
+	unsigned long last_updated;	/* In jiffies */
+	unsigned short addr;		/* uguru base address */
+	char uguru_ready;		/* is the uguru in ready state? */
+	unsigned char update_timeouts;	/* number of update timeouts since last
+					   successful update */
+
+	/* The sysfs attr and their names are generated automatically, for bank1
+	   we cannot use a predefined array because we don't know beforehand
+	   of a sensor is a volt or a temp sensor, for bank2 and the pwms its
+	   easier todo things the same way.  For in sensors we have 9 (temp 7)
+	   sysfs entries per sensor, for bank2 and pwms 6. */
+	struct sensor_device_attribute_2 sysfs_attr[16 * 9 +
+		ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6];
+	/* Buffer to store the dynamically generated sysfs names, we need 2120
+	   bytes for bank1 (worst case scenario of 16 in sensors), 444 bytes
+	   for fan1-6 and 738 bytes for pwm1-6 + some room to spare in case I
+	   miscounted :) */
+	char bank1_names[3400];
+
+	/* Bank 1 data */
+	u8 bank1_sensors[2];	/* number of [0] in, [1] temp sensors */
+	u8 bank1_address[2][16];/* addresses of [0] in, [1] temp sensors */
+	u8 bank1_value[16];
+	/* This array holds 16 x 3 entries for all the bank 1 sensor settings
+	   (flags, min, max for voltage / flags, warn, shutdown for temp). */
+	u8 bank1_settings[16][3];
+	/* Maximum value for each sensor used for scaling in mV/millidegrees
+	   Celsius. */
+	int bank1_max_value[16];
+
+	/* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */
+	u8 bank2_sensors; /* actual number of bank2 sensors found */
+	u8 bank2_value[ABIT_UGURU_MAX_BANK2_SENSORS];
+	u8 bank2_settings[ABIT_UGURU_MAX_BANK2_SENSORS][2]; /* flags, min */
+
+	/* Alarms 2 bytes for bank1, 1 byte for bank2 */
+	u8 alarms[3];
+
+	/* Fan PWM (speed control) 5 bytes per PWM */
+	u8 pwms; /* actual number of pwms found */
+	u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5];
+};
+
+/* wait till the uguru is in the specified state */
+static int abituguru_wait(struct abituguru_data *data, u8 state)
+{
+	int timeout = ABIT_UGURU_WAIT_TIMEOUT;
+
+	while (inb_p(data->addr + ABIT_UGURU_DATA) != state) {
+		timeout--;
+		if (timeout == 0)
+			return -EBUSY;
+	}
+	return 0;
+}
+
+/* Put the uguru in ready for input state */
+static int abituguru_ready(struct abituguru_data *data)
+{
+	int timeout = ABIT_UGURU_READY_TIMEOUT;
+
+	if (data->uguru_ready)
+		return 0;
+
+	/* Reset? / Prepare for next read/write cycle */
+	outb(0x00, data->addr + ABIT_UGURU_DATA);
+
+	/* Wait till the uguru is ready */
+	if (abituguru_wait(data, ABIT_UGURU_STATUS_READY)) {
+		ABIT_UGURU_DEBUG(1,
+			"timeout exceeded waiting for ready state\n");
+		return -EIO;
+	}
+
+	/* Cmd port MUST be read now and should contain 0xAC */
+	while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
+		timeout--;
+		if (timeout == 0) {
+			ABIT_UGURU_DEBUG(1,
+			   "CMD reg does not hold 0xAC after ready command\n");
+			return -EIO;
+		}
+	}
+
+	/* After this the ABIT_UGURU_DATA port should contain
+	   ABIT_UGURU_STATUS_INPUT */
+	timeout = ABIT_UGURU_READY_TIMEOUT;
+	while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) {
+		timeout--;
+		if (timeout == 0) {
+			ABIT_UGURU_DEBUG(1,
+				"state != more input after ready command\n");
+			return -EIO;
+		}
+	}
+
+	data->uguru_ready = 1;
+	return 0;
+}
+
+/* Send the bank and then sensor address to the uGuru for the next read/write
+   cycle. This function gets called as the first part of a read/write by
+   abituguru_read and abituguru_write. This function should never be
+   called by any other function. */
+static int abituguru_send_address(struct abituguru_data *data,
+	u8 bank_addr, u8 sensor_addr, int retries)
+{
+	/* assume the caller does error handling itself if it has not requested
+	   any retries, and thus be quiet. */
+	int report_errors = retries;
+
+	for (;;) {
+		/* Make sure the uguru is ready and then send the bank address,
+		   after this the uguru is no longer "ready". */
+		if (abituguru_ready(data) != 0)
+			return -EIO;
+		outb(bank_addr, data->addr + ABIT_UGURU_DATA);
+		data->uguru_ready = 0;
+
+		/* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again
+		   and send the sensor addr */
+		if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) {
+			if (retries) {
+				ABIT_UGURU_DEBUG(3, "timeout exceeded "
+					"waiting for more input state, %d "
+					"tries remaining\n", retries);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(ABIT_UGURU_RETRY_DELAY);
+				retries--;
+				continue;
+			}
+			if (report_errors)
+				ABIT_UGURU_DEBUG(1, "timeout exceeded "
+					"waiting for more input state "
+					"(bank: %d)\n", (int)bank_addr);
+			return -EBUSY;
+		}
+		outb(sensor_addr, data->addr + ABIT_UGURU_CMD);
+		return 0;
+	}
+}
+
+/* Read count bytes from sensor sensor_addr in bank bank_addr and store the
+   result in buf, retry the send address part of the read retries times. */
+static int abituguru_read(struct abituguru_data *data,
+	u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries)
+{
+	int i;
+
+	/* Send the address */
+	i = abituguru_send_address(data, bank_addr, sensor_addr, retries);
+	if (i)
+		return i;
+
+	/* And read the data */
+	for (i = 0; i < count; i++) {
+		if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
+			ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for "
+				"read state (bank: %d, sensor: %d)\n",
+				(int)bank_addr, (int)sensor_addr);
+			break;
+		}
+		buf[i] = inb(data->addr + ABIT_UGURU_CMD);
+	}
+
+	/* Last put the chip back in ready state */
+	abituguru_ready(data);
+
+	return i;
+}
+
+/* Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send
+   address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. */
+static int abituguru_write(struct abituguru_data *data,
+	u8 bank_addr, u8 sensor_addr, u8 *buf, int count)
+{
+	int i;
+
+	/* Send the address */
+	i = abituguru_send_address(data, bank_addr, sensor_addr,
+		ABIT_UGURU_MAX_RETRIES);
+	if (i)
+		return i;
+
+	/* And write the data */
+	for (i = 0; i < count; i++) {
+		if (abituguru_wait(data, ABIT_UGURU_STATUS_WRITE)) {
+			ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for "
+				"write state (bank: %d, sensor: %d)\n",
+				(int)bank_addr, (int)sensor_addr);
+			break;
+		}
+		outb(buf[i], data->addr + ABIT_UGURU_CMD);
+	}
+
+	/* Now we need to wait till the chip is ready to be read again,
+	   don't ask why */
+	if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
+		ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state "
+			"after write (bank: %d, sensor: %d)\n", (int)bank_addr,
+			(int)sensor_addr);
+		return -EIO;
+	}
+
+	/* Cmd port MUST be read now and should contain 0xAC */
+	if (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
+		ABIT_UGURU_DEBUG(1, "CMD reg does not hold 0xAC after write "
+			"(bank: %d, sensor: %d)\n", (int)bank_addr,
+			(int)sensor_addr);
+		return -EIO;
+	}
+
+	/* Last put the chip back in ready state */
+	abituguru_ready(data);
+
+	return i;
+}
+
+/* Detect sensor type. Temp and Volt sensors are enabled with
+   different masks and will ignore enable masks not meant for them.
+   This enables us to test what kind of sensor we're dealing with.
+   By setting the alarm thresholds so that we will always get an
+   alarm for sensor type X and then enabling the sensor as sensor type
+   X, if we then get an alarm it is a sensor of type X. */
+static int __devinit
+abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
+				   u8 sensor_addr)
+{
+	u8 val, buf[3];
+	int ret = ABIT_UGURU_NC;
+
+	/* First read the sensor and the current settings */
+	if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val,
+			1, ABIT_UGURU_MAX_RETRIES) != 1)
+		return -EIO;
+
+	/* Test val is sane / usable for sensor type detection. */
+	if ((val < 10u) || (val > 240u)) {
+		printk(KERN_WARNING ABIT_UGURU_NAME
+			": bank1-sensor: %d reading (%d) too close to limits, "
+			"unable to determine sensor type, skipping sensor\n",
+			(int)sensor_addr, (int)val);
+		/* assume no sensor is there for sensors for which we can't
+		   determine the sensor type because their reading is too close
+		   to their limits, this usually means no sensor is there. */
+		return ABIT_UGURU_NC;
+	}
+
+	ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr);
+	/* Volt sensor test, enable volt low alarm, set min value ridicously
+	   high. If its a volt sensor this should always give us an alarm. */
+	buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE;
+	buf[1] = 245;
+	buf[2] = 250;
+	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
+			buf, 3) != 3)
+		return -EIO;
+	/* Now we need 20 ms to give the uguru time to read the sensors
+	   and raise a voltage alarm */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/50);
+	/* Check for alarm and check the alarm is a volt low alarm. */
+	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
+			ABIT_UGURU_MAX_RETRIES) != 3)
+		return -EIO;
+	if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
+				sensor_addr, buf, 3,
+				ABIT_UGURU_MAX_RETRIES) != 3)
+			return -EIO;
+		if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) {
+			/* Restore original settings */
+			if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
+					sensor_addr,
+					data->bank1_settings[sensor_addr],
+					3) != 3)
+				return -EIO;
+			ABIT_UGURU_DEBUG(2, "  found volt sensor\n");
+			return ABIT_UGURU_IN_SENSOR;
+		} else
+			ABIT_UGURU_DEBUG(2, "  alarm raised during volt "
+				"sensor test, but volt low flag not set\n");
+	} else
+		ABIT_UGURU_DEBUG(2, "  alarm not raised during volt sensor "
+			"test\n");
+
+	/* Temp sensor test, enable sensor as a temp sensor, set beep value
+	   ridicously low (but not too low, otherwise uguru ignores it).
+	   If its a temp sensor this should always give us an alarm. */
+	buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE;
+	buf[1] = 5;
+	buf[2] = 10;
+	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
+			buf, 3) != 3)
+		return -EIO;
+	/* Now we need 50 ms to give the uguru time to read the sensors
+	   and raise a temp alarm */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/20);
+	/* Check for alarm and check the alarm is a temp high alarm. */
+	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
+			ABIT_UGURU_MAX_RETRIES) != 3)
+		return -EIO;
+	if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
+				sensor_addr, buf, 3,
+				ABIT_UGURU_MAX_RETRIES) != 3)
+			return -EIO;
+		if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) {
+			ret = ABIT_UGURU_TEMP_SENSOR;
+			ABIT_UGURU_DEBUG(2, "  found temp sensor\n");
+		} else
+			ABIT_UGURU_DEBUG(2, "  alarm raised during temp "
+				"sensor test, but temp high flag not set\n");
+	} else
+		ABIT_UGURU_DEBUG(2, "  alarm not raised during temp sensor "
+			"test\n");
+
+	/* Restore original settings */
+	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
+			data->bank1_settings[sensor_addr], 3) != 3)
+		return -EIO;
+
+	return ret;
+}
+
+/* These functions try to find out how many sensors there are in bank2 and how
+   many pwms there are. The purpose of this is to make sure that we don't give
+   the user the possibility to change settings for non-existent sensors / pwm.
+   The uGuru will happily read / write whatever memory happens to be after the
+   memory storing the PWM settings when reading/writing to a PWM which is not
+   there. Notice even if we detect a PWM which doesn't exist we normally won't
+   write to it, unless the user tries to change the settings.
+
+   Although the uGuru allows reading (settings) from non existing bank2
+   sensors, my version of the uGuru does seem to stop writing to them, the
+   write function above aborts in this case with:
+   "CMD reg does not hold 0xAC after write"
+
+   Notice these 2 tests are non destructive iow read-only tests, otherwise
+   they would defeat their purpose. Although for the bank2_sensors detection a
+   read/write test would be feasible because of the reaction above, I've
+   however opted to stay on the safe side. */
+static void __devinit
+abituguru_detect_no_bank2_sensors(struct abituguru_data *data)
+{
+	int i;
+
+	if (fan_sensors) {
+		data->bank2_sensors = fan_sensors;
+		ABIT_UGURU_DEBUG(2, "assuming %d fan sensors because of "
+			"\"fan_sensors\" module param\n",
+			(int)data->bank2_sensors);
+		return;
+	}
+
+	ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n");
+	for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
+		/* 0x89 are the known used bits:
+		   -0x80 enable shutdown
+		   -0x08 enable beep
+		   -0x01 enable alarm
+		   All other bits should be 0, but on some motherboards
+		   0x40 (bit 6) is also high, at least for fan1 */
+		if ((!i && (data->bank2_settings[i][0] & ~0xC9)) ||
+		     (i && (data->bank2_settings[i][0] & ~0x89))) {
+			ABIT_UGURU_DEBUG(2, "  bank2 sensor %d does not seem "
+				"to be a fan sensor: settings[0] = %02X\n",
+				i, (unsigned int)data->bank2_settings[i][0]);
+			break;
+		}
+
+		/* check if the threshold is within the allowed range */
+		if (data->bank2_settings[i][1] <
+				abituguru_bank2_min_threshold) {
+			ABIT_UGURU_DEBUG(2, "  bank2 sensor %d does not seem "
+				"to be a fan sensor: the threshold (%d) is "
+				"below the minimum (%d)\n", i,
+				(int)data->bank2_settings[i][1],
+				(int)abituguru_bank2_min_threshold);
+			break;
+		}
+		if (data->bank2_settings[i][1] >
+				abituguru_bank2_max_threshold) {
+			ABIT_UGURU_DEBUG(2, "  bank2 sensor %d does not seem "
+				"to be a fan sensor: the threshold (%d) is "
+				"above the maximum (%d)\n", i,
+				(int)data->bank2_settings[i][1],
+				(int)abituguru_bank2_max_threshold);
+			break;
+		}
+	}
+
+	data->bank2_sensors = i;
+	ABIT_UGURU_DEBUG(2, " found: %d fan sensors\n",
+		(int)data->bank2_sensors);
+}
+
+static void __devinit
+abituguru_detect_no_pwms(struct abituguru_data *data)
+{
+	int i, j;
+
+	if (pwms) {
+		data->pwms = pwms;
+		ABIT_UGURU_DEBUG(2, "assuming %d PWM outputs because of "
+			"\"pwms\" module param\n", (int)data->pwms);
+		return;
+	}
+
+	ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n");
+	for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
+		/* 0x80 is the enable bit and the low
+		   nibble is which temp sensor to use,
+		   the other bits should be 0 */
+		if (data->pwm_settings[i][0] & ~0x8F) {
+			ABIT_UGURU_DEBUG(2, "  pwm channel %d does not seem "
+				"to be a pwm channel: settings[0] = %02X\n",
+				i, (unsigned int)data->pwm_settings[i][0]);
+			break;
+		}
+
+		/* the low nibble must correspond to one of the temp sensors
+		   we've found */
+		for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR];
+				j++) {
+			if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] ==
+					(data->pwm_settings[i][0] & 0x0F))
+				break;
+		}
+		if (j == data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
+			ABIT_UGURU_DEBUG(2, "  pwm channel %d does not seem "
+				"to be a pwm channel: %d is not a valid temp "
+				"sensor address\n", i,
+				data->pwm_settings[i][0] & 0x0F);
+			break;
+		}
+
+		/* check if all other settings are within the allowed range */
+		for (j = 1; j < 5; j++) {
+			u8 min;
+			/* special case pwm1 min pwm% */
+			if ((i == 0) && ((j == 1) || (j == 2)))
+				min = 77;
+			else
+				min = abituguru_pwm_min[j];
+			if (data->pwm_settings[i][j] < min) {
+				ABIT_UGURU_DEBUG(2, "  pwm channel %d does "
+					"not seem to be a pwm channel: "
+					"setting %d (%d) is below the minimum "
+					"value (%d)\n", i, j,
+					(int)data->pwm_settings[i][j],
+					(int)min);
+				goto abituguru_detect_no_pwms_exit;
+			}
+			if (data->pwm_settings[i][j] > abituguru_pwm_max[j]) {
+				ABIT_UGURU_DEBUG(2, "  pwm channel %d does "
+					"not seem to be a pwm channel: "
+					"setting %d (%d) is above the maximum "
+					"value (%d)\n", i, j,
+					(int)data->pwm_settings[i][j],
+					(int)abituguru_pwm_max[j]);
+				goto abituguru_detect_no_pwms_exit;
+			}
+		}
+
+		/* check that min temp < max temp and min pwm < max pwm */
+		if (data->pwm_settings[i][1] >= data->pwm_settings[i][2]) {
+			ABIT_UGURU_DEBUG(2, "  pwm channel %d does not seem "
+				"to be a pwm channel: min pwm (%d) >= "
+				"max pwm (%d)\n", i,
+				(int)data->pwm_settings[i][1],
+				(int)data->pwm_settings[i][2]);
+			break;
+		}
+		if (data->pwm_settings[i][3] >= data->pwm_settings[i][4]) {
+			ABIT_UGURU_DEBUG(2, "  pwm channel %d does not seem "
+				"to be a pwm channel: min temp (%d) >= "
+				"max temp (%d)\n", i,
+				(int)data->pwm_settings[i][3],
+				(int)data->pwm_settings[i][4]);
+			break;
+		}
+	}
+
+abituguru_detect_no_pwms_exit:
+	data->pwms = i;
+	ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms);
+}
+
+/* Following are the sysfs callback functions. These functions expect:
+   sensor_device_attribute_2->index:   sensor address/offset in the bank
+   sensor_device_attribute_2->nr:      register offset, bitmask or NA. */
+static struct abituguru_data *abituguru_update_device(struct device *dev);
+
+static ssize_t show_bank1_value(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = abituguru_update_device(dev);
+	if (!data)
+		return -EIO;
+	return sprintf(buf, "%d\n", (data->bank1_value[attr->index] *
+		data->bank1_max_value[attr->index] + 128) / 255);
+}
+
+static ssize_t show_bank1_setting(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n",
+		(data->bank1_settings[attr->index][attr->nr] *
+		data->bank1_max_value[attr->index] + 128) / 255);
+}
+
+static ssize_t show_bank2_value(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = abituguru_update_device(dev);
+	if (!data)
+		return -EIO;
+	return sprintf(buf, "%d\n", (data->bank2_value[attr->index] *
+		ABIT_UGURU_FAN_MAX + 128) / 255);
+}
+
+static ssize_t show_bank2_setting(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n",
+		(data->bank2_settings[attr->index][attr->nr] *
+		ABIT_UGURU_FAN_MAX + 128) / 255);
+}
+
+static ssize_t store_bank1_setting(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 abituguru_data *data = dev_get_drvdata(dev);
+	u8 val = (simple_strtoul(buf, NULL, 10) * 255 +
+		data->bank1_max_value[attr->index]/2) /
+		data->bank1_max_value[attr->index];
+	ssize_t ret = count;
+
+	mutex_lock(&data->update_lock);
+	if (data->bank1_settings[attr->index][attr->nr] != val) {
+		u8 orig_val = data->bank1_settings[attr->index][attr->nr];
+		data->bank1_settings[attr->index][attr->nr] = val;
+		if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
+				attr->index, data->bank1_settings[attr->index],
+				3) <= attr->nr) {
+			data->bank1_settings[attr->index][attr->nr] = orig_val;
+			ret = -EIO;
+		}
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t store_bank2_setting(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 abituguru_data *data = dev_get_drvdata(dev);
+	u8 val = (simple_strtoul(buf, NULL, 10)*255 + ABIT_UGURU_FAN_MAX/2) /
+		ABIT_UGURU_FAN_MAX;
+	ssize_t ret = count;
+
+	/* this check can be done before taking the lock */
+	if ((val < abituguru_bank2_min_threshold) ||
+			(val > abituguru_bank2_max_threshold))
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+	if (data->bank2_settings[attr->index][attr->nr] != val) {
+		u8 orig_val = data->bank2_settings[attr->index][attr->nr];
+		data->bank2_settings[attr->index][attr->nr] = val;
+		if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK2 + 2,
+				attr->index, data->bank2_settings[attr->index],
+				2) <= attr->nr) {
+			data->bank2_settings[attr->index][attr->nr] = orig_val;
+			ret = -EIO;
+		}
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t show_bank1_alarm(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = abituguru_update_device(dev);
+	if (!data)
+		return -EIO;
+	/* See if the alarm bit for this sensor is set, and if the
+	   alarm matches the type of alarm we're looking for (for volt
+	   it can be either low or high). The type is stored in a few
+	   readonly bits in the settings part of the relevant sensor.
+	   The bitmask of the type is passed to us in attr->nr. */
+	if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) &&
+			(data->bank1_settings[attr->index][0] & attr->nr))
+		return sprintf(buf, "1\n");
+	else
+		return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank2_alarm(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = abituguru_update_device(dev);
+	if (!data)
+		return -EIO;
+	if (data->alarms[2] & (0x01 << attr->index))
+		return sprintf(buf, "1\n");
+	else
+		return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank1_mask(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	if (data->bank1_settings[attr->index][0] & attr->nr)
+		return sprintf(buf, "1\n");
+	else
+		return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank2_mask(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	if (data->bank2_settings[attr->index][0] & attr->nr)
+		return sprintf(buf, "1\n");
+	else
+		return sprintf(buf, "0\n");
+}
+
+static ssize_t store_bank1_mask(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 abituguru_data *data = dev_get_drvdata(dev);
+	int mask = simple_strtoul(buf, NULL, 10);
+	ssize_t ret = count;
+	u8 orig_val;
+
+	mutex_lock(&data->update_lock);
+	orig_val = data->bank1_settings[attr->index][0];
+
+	if (mask)
+		data->bank1_settings[attr->index][0] |= attr->nr;
+	else
+		data->bank1_settings[attr->index][0] &= ~attr->nr;
+
+	if ((data->bank1_settings[attr->index][0] != orig_val) &&
+			(abituguru_write(data,
+			ABIT_UGURU_SENSOR_BANK1 + 2, attr->index,
+			data->bank1_settings[attr->index], 3) < 1)) {
+		data->bank1_settings[attr->index][0] = orig_val;
+		ret = -EIO;
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t store_bank2_mask(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 abituguru_data *data = dev_get_drvdata(dev);
+	int mask = simple_strtoul(buf, NULL, 10);
+	ssize_t ret = count;
+	u8 orig_val;
+
+	mutex_lock(&data->update_lock);
+	orig_val = data->bank2_settings[attr->index][0];
+
+	if (mask)
+		data->bank2_settings[attr->index][0] |= attr->nr;
+	else
+		data->bank2_settings[attr->index][0] &= ~attr->nr;
+
+	if ((data->bank2_settings[attr->index][0] != orig_val) &&
+			(abituguru_write(data,
+			ABIT_UGURU_SENSOR_BANK2 + 2, attr->index,
+			data->bank2_settings[attr->index], 2) < 1)) {
+		data->bank2_settings[attr->index][0] = orig_val;
+		ret = -EIO;
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+/* Fan PWM (speed control) */
+static ssize_t show_pwm_setting(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", data->pwm_settings[attr->index][attr->nr] *
+		abituguru_pwm_settings_multiplier[attr->nr]);
+}
+
+static ssize_t store_pwm_setting(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 abituguru_data *data = dev_get_drvdata(dev);
+	u8 min, val = (simple_strtoul(buf, NULL, 10) +
+		abituguru_pwm_settings_multiplier[attr->nr]/2) /
+		abituguru_pwm_settings_multiplier[attr->nr];
+	ssize_t ret = count;
+
+	/* special case pwm1 min pwm% */
+	if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2)))
+		min = 77;
+	else
+		min = abituguru_pwm_min[attr->nr];
+
+	/* this check can be done before taking the lock */
+	if ((val < min) || (val > abituguru_pwm_max[attr->nr]))
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+	/* this check needs to be done after taking the lock */
+	if ((attr->nr & 1) &&
+			(val >= data->pwm_settings[attr->index][attr->nr + 1]))
+		ret = -EINVAL;
+	else if (!(attr->nr & 1) &&
+			(val <= data->pwm_settings[attr->index][attr->nr - 1]))
+		ret = -EINVAL;
+	else if (data->pwm_settings[attr->index][attr->nr] != val) {
+		u8 orig_val = data->pwm_settings[attr->index][attr->nr];
+		data->pwm_settings[attr->index][attr->nr] = val;
+		if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
+				attr->index, data->pwm_settings[attr->index],
+				5) <= attr->nr) {
+			data->pwm_settings[attr->index][attr->nr] =
+				orig_val;
+			ret = -EIO;
+		}
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t show_pwm_sensor(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	int i;
+	/* We need to walk to the temp sensor addresses to find what
+	   the userspace id of the configured temp sensor is. */
+	for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++)
+		if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] ==
+				(data->pwm_settings[attr->index][0] & 0x0F))
+			return sprintf(buf, "%d\n", i+1);
+
+	return -ENXIO;
+}
+
+static ssize_t store_pwm_sensor(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 abituguru_data *data = dev_get_drvdata(dev);
+	unsigned long val = simple_strtoul(buf, NULL, 10) - 1;
+	ssize_t ret = count;
+
+	mutex_lock(&data->update_lock);
+	if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
+		u8 orig_val = data->pwm_settings[attr->index][0];
+		u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val];
+		data->pwm_settings[attr->index][0] &= 0xF0;
+		data->pwm_settings[attr->index][0] |= address;
+		if (data->pwm_settings[attr->index][0] != orig_val) {
+			if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
+					attr->index,
+					data->pwm_settings[attr->index],
+					5) < 1) {
+				data->pwm_settings[attr->index][0] = orig_val;
+				ret = -EIO;
+			}
+		}
+	}
+	else
+		ret = -EINVAL;
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	int res = 0;
+	if (data->pwm_settings[attr->index][0] & ABIT_UGURU_FAN_PWM_ENABLE)
+		res = 2;
+	return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t store_pwm_enable(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 abituguru_data *data = dev_get_drvdata(dev);
+	u8 orig_val, user_val = simple_strtoul(buf, NULL, 10);
+	ssize_t ret = count;
+
+	mutex_lock(&data->update_lock);
+	orig_val = data->pwm_settings[attr->index][0];
+	switch (user_val) {
+		case 0:
+			data->pwm_settings[attr->index][0] &=
+				~ABIT_UGURU_FAN_PWM_ENABLE;
+			break;
+		case 2:
+			data->pwm_settings[attr->index][0] |=
+				ABIT_UGURU_FAN_PWM_ENABLE;
+			break;
+		default:
+			ret = -EINVAL;
+	}
+	if ((data->pwm_settings[attr->index][0] != orig_val) &&
+			(abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
+			attr->index, data->pwm_settings[attr->index],
+			5) < 1)) {
+		data->pwm_settings[attr->index][0] = orig_val;
+		ret = -EIO;
+	}
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static ssize_t show_name(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	return sprintf(buf, "%s\n", ABIT_UGURU_NAME);
+}
+
+/* Sysfs attr templates, the real entries are generated automatically. */
+static const
+struct sensor_device_attribute_2 abituguru_sysfs_bank1_templ[2][9] = {
+	{
+	SENSOR_ATTR_2(in%d_input, 0444, show_bank1_value, NULL, 0, 0),
+	SENSOR_ATTR_2(in%d_min, 0644, show_bank1_setting,
+		store_bank1_setting, 1, 0),
+	SENSOR_ATTR_2(in%d_min_alarm, 0444, show_bank1_alarm, NULL,
+		ABIT_UGURU_VOLT_LOW_ALARM_FLAG, 0),
+	SENSOR_ATTR_2(in%d_max, 0644, show_bank1_setting,
+		store_bank1_setting, 2, 0),
+	SENSOR_ATTR_2(in%d_max_alarm, 0444, show_bank1_alarm, NULL,
+		ABIT_UGURU_VOLT_HIGH_ALARM_FLAG, 0),
+	SENSOR_ATTR_2(in%d_beep, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+	SENSOR_ATTR_2(in%d_shutdown, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+	SENSOR_ATTR_2(in%d_min_alarm_enable, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, 0),
+	SENSOR_ATTR_2(in%d_max_alarm_enable, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE, 0),
+	}, {
+	SENSOR_ATTR_2(temp%d_input, 0444, show_bank1_value, NULL, 0, 0),
+	SENSOR_ATTR_2(temp%d_alarm, 0444, show_bank1_alarm, NULL,
+		ABIT_UGURU_TEMP_HIGH_ALARM_FLAG, 0),
+	SENSOR_ATTR_2(temp%d_max, 0644, show_bank1_setting,
+		store_bank1_setting, 1, 0),
+	SENSOR_ATTR_2(temp%d_crit, 0644, show_bank1_setting,
+		store_bank1_setting, 2, 0),
+	SENSOR_ATTR_2(temp%d_beep, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+	SENSOR_ATTR_2(temp%d_shutdown, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+	SENSOR_ATTR_2(temp%d_alarm_enable, 0644, show_bank1_mask,
+		store_bank1_mask, ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, 0),
+	}
+};
+
+static const struct sensor_device_attribute_2 abituguru_sysfs_fan_templ[6] = {
+	SENSOR_ATTR_2(fan%d_input, 0444, show_bank2_value, NULL, 0, 0),
+	SENSOR_ATTR_2(fan%d_alarm, 0444, show_bank2_alarm, NULL, 0, 0),
+	SENSOR_ATTR_2(fan%d_min, 0644, show_bank2_setting,
+		store_bank2_setting, 1, 0),
+	SENSOR_ATTR_2(fan%d_beep, 0644, show_bank2_mask,
+		store_bank2_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+	SENSOR_ATTR_2(fan%d_shutdown, 0644, show_bank2_mask,
+		store_bank2_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+	SENSOR_ATTR_2(fan%d_alarm_enable, 0644, show_bank2_mask,
+		store_bank2_mask, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, 0),
+};
+
+static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = {
+	SENSOR_ATTR_2(pwm%d_enable, 0644, show_pwm_enable,
+		store_pwm_enable, 0, 0),
+	SENSOR_ATTR_2(pwm%d_auto_channels_temp, 0644, show_pwm_sensor,
+		store_pwm_sensor, 0, 0),
+	SENSOR_ATTR_2(pwm%d_auto_point1_pwm, 0644, show_pwm_setting,
+		store_pwm_setting, 1, 0),
+	SENSOR_ATTR_2(pwm%d_auto_point2_pwm, 0644, show_pwm_setting,
+		store_pwm_setting, 2, 0),
+	SENSOR_ATTR_2(pwm%d_auto_point1_temp, 0644, show_pwm_setting,
+		store_pwm_setting, 3, 0),
+	SENSOR_ATTR_2(pwm%d_auto_point2_temp, 0644, show_pwm_setting,
+		store_pwm_setting, 4, 0),
+};
+
+static const struct sensor_device_attribute_2 abituguru_sysfs_attr[] = {
+	SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
+};
+
+static int __devinit abituguru_probe(struct platform_device *pdev)
+{
+	struct abituguru_data *data;
+	int i, j, res;
+	char *sysfs_filename;
+	int sysfs_attr_i = 0;
+
+	/* El weirdo probe order, to keep the sysfs order identical to the
+	   BIOS and window-appliction listing order. */
+	const u8 probe_order[16] = { 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E,
+		0x02, 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
+
+	if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL)))
+		return -ENOMEM;
+
+	data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+	mutex_init(&data->update_lock);
+	platform_set_drvdata(pdev, data);
+
+	/* See if the uGuru is ready */
+	if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT)
+		data->uguru_ready = 1;
+
+	/* Completely read the uGuru this has 2 purposes:
+	   - testread / see if one really is there.
+	   - make an in memory copy of all the uguru settings for future use. */
+	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
+			data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) {
+		kfree(data);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < 16; i++) {
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i,
+				&data->bank1_value[i], 1,
+				ABIT_UGURU_MAX_RETRIES) != 1) {
+			kfree(data);
+			return -ENODEV;
+		}
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i,
+				data->bank1_settings[i], 3,
+				ABIT_UGURU_MAX_RETRIES) != 3) {
+			kfree(data);
+			return -ENODEV;
+		}
+	}
+	/* Note: We don't know how many bank2 sensors / pwms there really are,
+	   but in order to "detect" this we need to read the maximum amount
+	   anyways. If we read sensors/pwms not there we'll just read crap
+	   this can't hurt. We need the detection because we don't want
+	   unwanted writes, which will hurt! */
+	for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i,
+				&data->bank2_value[i], 1,
+				ABIT_UGURU_MAX_RETRIES) != 1) {
+			kfree(data);
+			return -ENODEV;
+		}
+		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i,
+				data->bank2_settings[i], 2,
+				ABIT_UGURU_MAX_RETRIES) != 2) {
+			kfree(data);
+			return -ENODEV;
+		}
+	}
+	for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
+		if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i,
+				data->pwm_settings[i], 5,
+				ABIT_UGURU_MAX_RETRIES) != 5) {
+			kfree(data);
+			return -ENODEV;
+		}
+	}
+	data->last_updated = jiffies;
+
+	/* Detect sensor types and fill the sysfs attr for bank1 */
+	sysfs_filename = data->bank1_names;
+	for (i = 0; i < 16; i++) {
+		res = abituguru_detect_bank1_sensor_type(data, probe_order[i]);
+		if (res < 0) {
+			kfree(data);
+			return -ENODEV;
+		}
+		if (res == ABIT_UGURU_NC)
+			continue;
+
+		for (j = 0; j < (res ? 7 : 9); j++) {
+			const char *name_templ = abituguru_sysfs_bank1_templ[
+				res][j].dev_attr.attr.name;
+			data->sysfs_attr[sysfs_attr_i] =
+				abituguru_sysfs_bank1_templ[res][j];
+			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+				sysfs_filename;
+			sysfs_filename += sprintf(sysfs_filename, name_templ,
+				data->bank1_sensors[res] + res) + 1;
+			data->sysfs_attr[sysfs_attr_i].index = probe_order[i];
+			sysfs_attr_i++;
+		}
+		data->bank1_max_value[probe_order[i]] =
+			abituguru_bank1_max_value[res];
+		data->bank1_address[res][data->bank1_sensors[res]] =
+			probe_order[i];
+		data->bank1_sensors[res]++;
+	}
+	/* Detect number of sensors and fill the sysfs attr for bank2 (fans) */
+	abituguru_detect_no_bank2_sensors(data);
+	for (i = 0; i < data->bank2_sensors; i++) {
+		for (j = 0; j < 6; j++) {
+			const char *name_templ = abituguru_sysfs_fan_templ[j].
+				dev_attr.attr.name;
+			data->sysfs_attr[sysfs_attr_i] =
+				abituguru_sysfs_fan_templ[j];
+			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+				sysfs_filename;
+			sysfs_filename += sprintf(sysfs_filename, name_templ,
+				i + 1) + 1;
+			data->sysfs_attr[sysfs_attr_i].index = i;
+			sysfs_attr_i++;
+		}
+	}
+	/* Detect number of sensors and fill the sysfs attr for pwms */
+	abituguru_detect_no_pwms(data);
+	for (i = 0; i < data->pwms; i++) {
+		for (j = 0; j < 6; j++) {
+			const char *name_templ = abituguru_sysfs_pwm_templ[j].
+				dev_attr.attr.name;
+			data->sysfs_attr[sysfs_attr_i] =
+				abituguru_sysfs_pwm_templ[j];
+			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+				sysfs_filename;
+			sysfs_filename += sprintf(sysfs_filename, name_templ,
+				i + 1) + 1;
+			data->sysfs_attr[sysfs_attr_i].index = i;
+			sysfs_attr_i++;
+		}
+	}
+	/* Last add any "generic" entries to sysfs */
+	for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) {
+		data->sysfs_attr[sysfs_attr_i] = abituguru_sysfs_attr[i];
+		sysfs_attr_i++;
+	}
+	printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n");
+
+	/* Register sysfs hooks */
+	data->class_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->class_dev)) {
+		kfree(data);
+		return PTR_ERR(data->class_dev);
+	}
+	for (i = 0; i < sysfs_attr_i; i++)
+		device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+
+	return 0;
+}
+
+static int __devexit abituguru_remove(struct platform_device *pdev)
+{
+	struct abituguru_data *data = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	hwmon_device_unregister(data->class_dev);
+	kfree(data);
+
+	return 0;
+}
+
+static struct abituguru_data *abituguru_update_device(struct device *dev)
+{
+	int i, err;
+	struct abituguru_data *data = dev_get_drvdata(dev);
+	/* fake a complete successful read if no update necessary. */
+	char success = 1;
+
+	mutex_lock(&data->update_lock);
+	if (time_after(jiffies, data->last_updated + HZ)) {
+		success = 0;
+		if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
+				data->alarms, 3, 0)) != 3)
+			goto LEAVE_UPDATE;
+		for (i = 0; i < 16; i++) {
+			if ((err = abituguru_read(data,
+					ABIT_UGURU_SENSOR_BANK1, i,
+					&data->bank1_value[i], 1, 0)) != 1)
+				goto LEAVE_UPDATE;
+			if ((err = abituguru_read(data,
+					ABIT_UGURU_SENSOR_BANK1 + 1, i,
+					data->bank1_settings[i], 3, 0)) != 3)
+				goto LEAVE_UPDATE;
+		}
+		for (i = 0; i < data->bank2_sensors; i++)
+			if ((err = abituguru_read(data,
+					ABIT_UGURU_SENSOR_BANK2, i,
+					&data->bank2_value[i], 1, 0)) != 1)
+				goto LEAVE_UPDATE;
+		/* success! */
+		success = 1;
+		data->update_timeouts = 0;
+LEAVE_UPDATE:
+		/* handle timeout condition */
+		if (err == -EBUSY) {
+			/* No overflow please */
+			if (data->update_timeouts < 255u)
+				data->update_timeouts++;
+			if (data->update_timeouts <= ABIT_UGURU_MAX_TIMEOUTS) {
+				ABIT_UGURU_DEBUG(3, "timeout exceeded, will "
+					"try again next update\n");
+				/* Just a timeout, fake a successful read */
+				success = 1;
+			} else
+				ABIT_UGURU_DEBUG(1, "timeout exceeded %d "
+					"times waiting for more input state\n",
+					(int)data->update_timeouts);
+		}
+		/* On success set last_updated */
+		if (success)
+			data->last_updated = jiffies;
+	}
+	mutex_unlock(&data->update_lock);
+
+	if (success)
+		return data;
+	else
+		return NULL;
+}
+
+static struct platform_driver abituguru_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= ABIT_UGURU_NAME,
+	},
+	.probe	= abituguru_probe,
+	.remove	= __devexit_p(abituguru_remove),
+};
+
+static int __init abituguru_detect(void)
+{
+	/* See if there is an uguru there. After a reboot uGuru will hold 0x00
+	   at DATA and 0xAC, when this driver has already been loaded once
+	   DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either
+	   scenario but some will hold 0x00.
+	   Some uGuru's initally hold 0x09 at DATA and will only hold 0x08
+	   after reading CMD first, so CMD must be read first! */
+	u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD);
+	u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA);
+	if (((data_val == 0x00) || (data_val == 0x08)) &&
+	    ((cmd_val == 0x00) || (cmd_val == 0xAC)))
+		return ABIT_UGURU_BASE;
+
+	ABIT_UGURU_DEBUG(2, "no Abit uGuru found, data = 0x%02X, cmd = "
+		"0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val);
+
+	if (force) {
+		printk(KERN_INFO ABIT_UGURU_NAME ": Assuming Abit uGuru is "
+				"present because of \"force\" parameter\n");
+		return ABIT_UGURU_BASE;
+	}
+
+	/* No uGuru found */
+	return -ENODEV;
+}
+
+static struct platform_device *abituguru_pdev;
+
+static int __init abituguru_init(void)
+{
+	int address, err;
+	struct resource res = { .flags = IORESOURCE_IO };
+
+	address = abituguru_detect();
+	if (address < 0)
+		return address;
+
+	err = platform_driver_register(&abituguru_driver);
+	if (err)
+		goto exit;
+
+	abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address);
+	if (!abituguru_pdev) {
+		printk(KERN_ERR ABIT_UGURU_NAME
+			": Device allocation failed\n");
+		err = -ENOMEM;
+		goto exit_driver_unregister;
+	}
+
+	res.start = address;
+	res.end = address + ABIT_UGURU_REGION_LENGTH - 1;
+	res.name = ABIT_UGURU_NAME;
+
+	err = platform_device_add_resources(abituguru_pdev, &res, 1);
+	if (err) {
+		printk(KERN_ERR ABIT_UGURU_NAME
+			": Device resource addition failed (%d)\n", err);
+		goto exit_device_put;
+	}
+
+	err = platform_device_add(abituguru_pdev);
+	if (err) {
+		printk(KERN_ERR ABIT_UGURU_NAME
+			": Device addition failed (%d)\n", err);
+		goto exit_device_put;
+	}
+
+	return 0;
+
+exit_device_put:
+	platform_device_put(abituguru_pdev);
+exit_driver_unregister:
+	platform_driver_unregister(&abituguru_driver);
+exit:
+	return err;
+}
+
+static void __exit abituguru_exit(void)
+{
+	platform_device_unregister(abituguru_pdev);
+	platform_driver_unregister(&abituguru_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_DESCRIPTION("Abit uGuru Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(abituguru_init);
+module_exit(abituguru_exit);
-- 
cgit v1.1


From a2392e0b907b9633c31be14ee75bb39fce348b01 Mon Sep 17 00:00:00 2001
From: Hans de Goede <j.w.r.degoede@hhs.nl>
Date: Sun, 4 Jun 2006 20:23:01 +0200
Subject: [PATCH] abituguru: Review fixes

Fixes to the Abit uGuru driver as requested in review by Jean Delvare:
 - exactly calculate the sysfs_names array length using macro
 - use snprintf when generating names to double check that the sysfs_names
   array does not overflow.
 - use ARRAY_SIZE and / or defines to determine number of loops in for loops
   instead of using hardcoded values.
 - In abituguru_probe(), refactor the error path leaving a single call to kfree

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/abituguru.c | 197 ++++++++++++++++++++++++++--------------------
 1 file changed, 111 insertions(+), 86 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index bf2cb0a..ab80b41 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -36,6 +36,10 @@
 #define ABIT_UGURU_SENSOR_BANK1			0x21 /* 16x volt and temp */
 #define ABIT_UGURU_FAN_PWM			0x24 /* 3x 5 bytes */
 #define ABIT_UGURU_SENSOR_BANK2			0x26 /* fans */
+/* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */
+#define ABIT_UGURU_MAX_BANK1_SENSORS		16
+/* Warning if you increase one of the 2 MAX defines below to 10 or higher you
+   should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! */
 /* max nr of sensors in bank2, currently mb's with max 6 fans are known */
 #define ABIT_UGURU_MAX_BANK2_SENSORS		6
 /* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */
@@ -74,10 +78,33 @@
 /* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */
 #define ABIT_UGURU_MAX_RETRIES			3
 #define ABIT_UGURU_RETRY_DELAY			(HZ/5)
-/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is a error */
+/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is an error */
 #define ABIT_UGURU_MAX_TIMEOUTS			2
-
-/* All the variables below are named identical to the oguru and oguru2 programs
+/* utility macros */
+#define ABIT_UGURU_NAME				"abituguru"
+#define ABIT_UGURU_DEBUG(level, format, arg...)				\
+	if (level <= verbose)						\
+		printk(KERN_DEBUG ABIT_UGURU_NAME ": "	format , ## arg)
+/* Macros to help calculate the sysfs_names array length */
+/* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
+   in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 */
+#define ABITUGURU_IN_NAMES_LENGTH	(11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14)
+/* sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0,
+   temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 */
+#define ABITUGURU_TEMP_NAMES_LENGTH	(13 + 11 + 12 + 13 + 20 + 12 + 16)
+/* sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0,
+   fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 */
+#define ABITUGURU_FAN_NAMES_LENGTH	(11 + 9 + 11 + 18 + 10 + 14)
+/* sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0,
+   pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 */
+#define ABITUGURU_PWM_NAMES_LENGTH	(12 + 24 + 2 * 21 + 2 * 22)
+/* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */
+#define ABITUGURU_SYSFS_NAMES_LENGTH	( \
+	ABIT_UGURU_MAX_BANK1_SENSORS * ABITUGURU_IN_NAMES_LENGTH + \
+	ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \
+	ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH)
+
+/* All the macros below are named identical to the oguru and oguru2 programs
    reverse engineered by Olle Sandberg, hence the names might not be 100%
    logical. I could come up with better names, but I prefer keeping the names
    identical so that this driver can be compared with his work more easily. */
@@ -93,11 +120,6 @@
 #define ABIT_UGURU_STATUS_READ			0x01 /* Ready to be read */
 #define ABIT_UGURU_STATUS_INPUT			0x08 /* More input */
 #define ABIT_UGURU_STATUS_READY			0x09 /* Ready to be written */
-/* utility macros */
-#define ABIT_UGURU_NAME				"abituguru"
-#define ABIT_UGURU_DEBUG(level, format, arg...)				\
-	if (level <= verbose)						\
-		printk(KERN_DEBUG ABIT_UGURU_NAME ": "	format , ## arg)
 
 /* Constants */
 /* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */
@@ -156,24 +178,23 @@ struct abituguru_data {
 	   of a sensor is a volt or a temp sensor, for bank2 and the pwms its
 	   easier todo things the same way.  For in sensors we have 9 (temp 7)
 	   sysfs entries per sensor, for bank2 and pwms 6. */
-	struct sensor_device_attribute_2 sysfs_attr[16 * 9 +
+	struct sensor_device_attribute_2 sysfs_attr[
+		ABIT_UGURU_MAX_BANK1_SENSORS * 9 +
 		ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6];
-	/* Buffer to store the dynamically generated sysfs names, we need 2120
-	   bytes for bank1 (worst case scenario of 16 in sensors), 444 bytes
-	   for fan1-6 and 738 bytes for pwm1-6 + some room to spare in case I
-	   miscounted :) */
-	char bank1_names[3400];
+	/* Buffer to store the dynamically generated sysfs names */
+	char sysfs_names[ABITUGURU_SYSFS_NAMES_LENGTH];
 
 	/* Bank 1 data */
-	u8 bank1_sensors[2];	/* number of [0] in, [1] temp sensors */
-	u8 bank1_address[2][16];/* addresses of [0] in, [1] temp sensors */
-	u8 bank1_value[16];
-	/* This array holds 16 x 3 entries for all the bank 1 sensor settings
+	/* number of and addresses of [0] in, [1] temp sensors */
+	u8 bank1_sensors[2];
+	u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS];
+	u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS];
+	/* This array holds 3 entries per sensor for the bank 1 sensor settings
 	   (flags, min, max for voltage / flags, warn, shutdown for temp). */
-	u8 bank1_settings[16][3];
+	u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3];
 	/* Maximum value for each sensor used for scaling in mV/millidegrees
 	   Celsius. */
-	int bank1_max_value[16];
+	int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS];
 
 	/* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */
 	u8 bank2_sensors; /* actual number of bank2 sensors found */
@@ -379,7 +400,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	/* First read the sensor and the current settings */
 	if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val,
 			1, ABIT_UGURU_MAX_RETRIES) != 1)
-		return -EIO;
+		return -ENODEV;
 
 	/* Test val is sane / usable for sensor type detection. */
 	if ((val < 10u) || (val > 240u)) {
@@ -401,7 +422,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	buf[2] = 250;
 	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
 			buf, 3) != 3)
-		return -EIO;
+		return -ENODEV;
 	/* Now we need 20 ms to give the uguru time to read the sensors
 	   and raise a voltage alarm */
 	set_current_state(TASK_UNINTERRUPTIBLE);
@@ -409,19 +430,19 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	/* Check for alarm and check the alarm is a volt low alarm. */
 	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
 			ABIT_UGURU_MAX_RETRIES) != 3)
-		return -EIO;
+		return -ENODEV;
 	if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
 				sensor_addr, buf, 3,
 				ABIT_UGURU_MAX_RETRIES) != 3)
-			return -EIO;
+			return -ENODEV;
 		if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) {
 			/* Restore original settings */
 			if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
 					sensor_addr,
 					data->bank1_settings[sensor_addr],
 					3) != 3)
-				return -EIO;
+				return -ENODEV;
 			ABIT_UGURU_DEBUG(2, "  found volt sensor\n");
 			return ABIT_UGURU_IN_SENSOR;
 		} else
@@ -439,7 +460,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	buf[2] = 10;
 	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
 			buf, 3) != 3)
-		return -EIO;
+		return -ENODEV;
 	/* Now we need 50 ms to give the uguru time to read the sensors
 	   and raise a temp alarm */
 	set_current_state(TASK_UNINTERRUPTIBLE);
@@ -447,12 +468,12 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	/* Check for alarm and check the alarm is a temp high alarm. */
 	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
 			ABIT_UGURU_MAX_RETRIES) != 3)
-		return -EIO;
+		return -ENODEV;
 	if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
 				sensor_addr, buf, 3,
 				ABIT_UGURU_MAX_RETRIES) != 3)
-			return -EIO;
+			return -ENODEV;
 		if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) {
 			ret = ABIT_UGURU_TEMP_SENSOR;
 			ABIT_UGURU_DEBUG(2, "  found temp sensor\n");
@@ -466,7 +487,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
 	/* Restore original settings */
 	if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
 			data->bank1_settings[sensor_addr], 3) != 3)
-		return -EIO;
+		return -ENODEV;
 
 	return ret;
 }
@@ -1061,21 +1082,21 @@ static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = {
 		store_pwm_setting, 4, 0),
 };
 
-static const struct sensor_device_attribute_2 abituguru_sysfs_attr[] = {
+static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = {
 	SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
 };
 
 static int __devinit abituguru_probe(struct platform_device *pdev)
 {
 	struct abituguru_data *data;
-	int i, j, res;
+	int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV;
 	char *sysfs_filename;
-	int sysfs_attr_i = 0;
 
 	/* El weirdo probe order, to keep the sysfs order identical to the
 	   BIOS and window-appliction listing order. */
-	const u8 probe_order[16] = { 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E,
-		0x02, 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
+	const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = {
+		0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02,
+		0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
 
 	if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL)))
 		return -ENOMEM;
@@ -1092,24 +1113,18 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
 	   - testread / see if one really is there.
 	   - make an in memory copy of all the uguru settings for future use. */
 	if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
-			data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) {
-		kfree(data);
-		return -ENODEV;
-	}
+			data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3)
+		goto abituguru_probe_error;
 
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i,
 				&data->bank1_value[i], 1,
-				ABIT_UGURU_MAX_RETRIES) != 1) {
-			kfree(data);
-			return -ENODEV;
-		}
+				ABIT_UGURU_MAX_RETRIES) != 1)
+			goto abituguru_probe_error;
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i,
 				data->bank1_settings[i], 3,
-				ABIT_UGURU_MAX_RETRIES) != 3) {
-			kfree(data);
-			return -ENODEV;
-		}
+				ABIT_UGURU_MAX_RETRIES) != 3)
+			goto abituguru_probe_error;
 	}
 	/* Note: We don't know how many bank2 sensors / pwms there really are,
 	   but in order to "detect" this we need to read the maximum amount
@@ -1119,48 +1134,45 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
 	for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i,
 				&data->bank2_value[i], 1,
-				ABIT_UGURU_MAX_RETRIES) != 1) {
-			kfree(data);
-			return -ENODEV;
-		}
+				ABIT_UGURU_MAX_RETRIES) != 1)
+			goto abituguru_probe_error;
 		if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i,
 				data->bank2_settings[i], 2,
-				ABIT_UGURU_MAX_RETRIES) != 2) {
-			kfree(data);
-			return -ENODEV;
-		}
+				ABIT_UGURU_MAX_RETRIES) != 2)
+			goto abituguru_probe_error;
 	}
 	for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
 		if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i,
 				data->pwm_settings[i], 5,
-				ABIT_UGURU_MAX_RETRIES) != 5) {
-			kfree(data);
-			return -ENODEV;
-		}
+				ABIT_UGURU_MAX_RETRIES) != 5)
+			goto abituguru_probe_error;
 	}
 	data->last_updated = jiffies;
 
 	/* Detect sensor types and fill the sysfs attr for bank1 */
-	sysfs_filename = data->bank1_names;
-	for (i = 0; i < 16; i++) {
+	sysfs_attr_i = 0;
+	sysfs_filename = data->sysfs_names;
+	sysfs_names_free = ABITUGURU_SYSFS_NAMES_LENGTH;
+	for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
 		res = abituguru_detect_bank1_sensor_type(data, probe_order[i]);
-		if (res < 0) {
-			kfree(data);
-			return -ENODEV;
-		}
+		if (res < 0)
+			goto abituguru_probe_error;
 		if (res == ABIT_UGURU_NC)
 			continue;
 
+		/* res 1 (temp) sensors have 7 sysfs entries, 0 (in) 9 */
 		for (j = 0; j < (res ? 7 : 9); j++) {
-			const char *name_templ = abituguru_sysfs_bank1_templ[
-				res][j].dev_attr.attr.name;
+			used = snprintf(sysfs_filename, sysfs_names_free,
+				abituguru_sysfs_bank1_templ[res][j].dev_attr.
+				attr.name, data->bank1_sensors[res] + res)
+				+ 1;
 			data->sysfs_attr[sysfs_attr_i] =
 				abituguru_sysfs_bank1_templ[res][j];
 			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
 				sysfs_filename;
-			sysfs_filename += sprintf(sysfs_filename, name_templ,
-				data->bank1_sensors[res] + res) + 1;
 			data->sysfs_attr[sysfs_attr_i].index = probe_order[i];
+			sysfs_filename += used;
+			sysfs_names_free -= used;
 			sysfs_attr_i++;
 		}
 		data->bank1_max_value[probe_order[i]] =
@@ -1172,52 +1184,65 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
 	/* Detect number of sensors and fill the sysfs attr for bank2 (fans) */
 	abituguru_detect_no_bank2_sensors(data);
 	for (i = 0; i < data->bank2_sensors; i++) {
-		for (j = 0; j < 6; j++) {
-			const char *name_templ = abituguru_sysfs_fan_templ[j].
-				dev_attr.attr.name;
+		for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_fan_templ); j++) {
+			used = snprintf(sysfs_filename, sysfs_names_free,
+				abituguru_sysfs_fan_templ[j].dev_attr.attr.name,
+				i + 1) + 1;
 			data->sysfs_attr[sysfs_attr_i] =
 				abituguru_sysfs_fan_templ[j];
 			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
 				sysfs_filename;
-			sysfs_filename += sprintf(sysfs_filename, name_templ,
-				i + 1) + 1;
 			data->sysfs_attr[sysfs_attr_i].index = i;
+			sysfs_filename += used;
+			sysfs_names_free -= used;
 			sysfs_attr_i++;
 		}
 	}
 	/* Detect number of sensors and fill the sysfs attr for pwms */
 	abituguru_detect_no_pwms(data);
 	for (i = 0; i < data->pwms; i++) {
-		for (j = 0; j < 6; j++) {
-			const char *name_templ = abituguru_sysfs_pwm_templ[j].
-				dev_attr.attr.name;
+		for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_pwm_templ); j++) {
+			used = snprintf(sysfs_filename, sysfs_names_free,
+				abituguru_sysfs_pwm_templ[j].dev_attr.attr.name,
+				i + 1) + 1;
 			data->sysfs_attr[sysfs_attr_i] =
 				abituguru_sysfs_pwm_templ[j];
 			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
 				sysfs_filename;
-			sysfs_filename += sprintf(sysfs_filename, name_templ,
-				i + 1) + 1;
 			data->sysfs_attr[sysfs_attr_i].index = i;
+			sysfs_filename += used;
+			sysfs_names_free -= used;
 			sysfs_attr_i++;
 		}
 	}
-	/* Last add any "generic" entries to sysfs */
-	for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) {
-		data->sysfs_attr[sysfs_attr_i] = abituguru_sysfs_attr[i];
-		sysfs_attr_i++;
+	/* Fail safe check, this should never happen! */
+	if (sysfs_names_free < 0) {
+		printk(KERN_ERR ABIT_UGURU_NAME ": Fatal error ran out of "
+		       "space for sysfs attr names. This should never "
+		       "happen please report to the abituguru maintainer "
+		       "(see MAINTAINERS)\n");
+		res = -ENAMETOOLONG;
+		goto abituguru_probe_error;
 	}
 	printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n");
 
 	/* Register sysfs hooks */
 	data->class_dev = hwmon_device_register(&pdev->dev);
 	if (IS_ERR(data->class_dev)) {
-		kfree(data);
-		return PTR_ERR(data->class_dev);
+		res = PTR_ERR(data->class_dev);
+		goto abituguru_probe_error;
 	}
 	for (i = 0; i < sysfs_attr_i; i++)
 		device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+	for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
+		device_create_file(&pdev->dev,
+			&abituguru_sysfs_attr[i].dev_attr);
 
 	return 0;
+
+abituguru_probe_error:
+	kfree(data);
+	return res;
 }
 
 static int __devexit abituguru_remove(struct platform_device *pdev)
@@ -1244,7 +1269,7 @@ static struct abituguru_data *abituguru_update_device(struct device *dev)
 		if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
 				data->alarms, 3, 0)) != 3)
 			goto LEAVE_UPDATE;
-		for (i = 0; i < 16; i++) {
+		for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
 			if ((err = abituguru_read(data,
 					ABIT_UGURU_SENSOR_BANK1, i,
 					&data->bank1_value[i], 1, 0)) != 1)
-- 
cgit v1.1


From b7c066044f4355d9fbe1ac45e03303b78d21e8d4 Mon Sep 17 00:00:00 2001
From: Hans de Goede <j.w.r.degoede@hhs.nl>
Date: Sun, 4 Jun 2006 20:24:11 +0200
Subject: [PATCH] abituguru: Fix fan detection

One of my testers had a problem where the driver only saw 2 of the 4 fan
sensors his uGuru has, this fixes this.
 -accept 0x40 (bit 6) being high as a valid fan sensor setting for all fans
  not just fan 1, I have a feeling this bit indicates whether or not a fan is
  actually connected .

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/abituguru.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index ab80b41..59122cc 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -529,9 +529,8 @@ abituguru_detect_no_bank2_sensors(struct abituguru_data *data)
 		   -0x08 enable beep
 		   -0x01 enable alarm
 		   All other bits should be 0, but on some motherboards
-		   0x40 (bit 6) is also high, at least for fan1 */
-		if ((!i && (data->bank2_settings[i][0] & ~0xC9)) ||
-		     (i && (data->bank2_settings[i][0] & ~0x89))) {
+		   0x40 (bit 6) is also high for some of the fans?? */
+		if (data->bank2_settings[i][0] & ~0xC9) {
 			ABIT_UGURU_DEBUG(2, "  bank2 sensor %d does not seem "
 				"to be a fan sensor: settings[0] = %02X\n",
 				i, (unsigned int)data->bank2_settings[i][0]);
-- 
cgit v1.1


From 060b2f5584bf3bbb0268c83c24d5a506780fb702 Mon Sep 17 00:00:00 2001
From: Jean Delvare <khali@linux-fr.org>
Date: Mon, 12 Jun 2006 21:55:28 +0200
Subject: [PATCH] hwmon: Fix the Kconfig header

Hardware monitoring chips don't have to be on the I2C bus.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6fb93d6..43e1f08 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1,5 +1,5 @@
 #
-# I2C Sensor chip drivers configuration
+# Hardware monitoring chip drivers configuration
 #
 
 menu "Hardware Monitoring support"
-- 
cgit v1.1


From e1a8e913f97e36cc5a23a24a8b4717e84998f13c Mon Sep 17 00:00:00 2001
From: Kaiwan N Billimoria <kaiwan@designergraphix.com>
Date: Mon, 12 Jun 2006 22:00:05 +0200
Subject: [PATCH] lm70: New hardware monitoring driver

This driver implements support for the National Semiconductor LM70
temperature sensor.

The LM70 temperature sensor chip supports a single temperature sensor.
It communicates with a host processor (or microcontroller) via an
SPI/Microwire Bus interface.

Communication with the LM70 is simple: when the temperature is to be sensed,
the driver accesses the LM70 using SPI communication: 16 SCLK cycles
comprise the MOSI/MISO loop. At the end of the transfer, the 11-bit 2's
complement digital temperature (sent via the SIO line), is available in the
driver for interpretation. This driver makes use of the kernel's in-core
SPI support.

Signed-off-by: Kaiwan N Billimoria <kaiwan@designergraphix.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/hwmon/Kconfig  |  10 +++
 drivers/hwmon/Makefile |   1 +
 drivers/hwmon/lm70.c   | 165 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 176 insertions(+)
 create mode 100644 drivers/hwmon/lm70.c

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 43e1f08..0e31a0c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -204,6 +204,16 @@ config SENSORS_LM63
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm63.
 
+config SENSORS_LM70
+	tristate "National Semiconductor LM70"
+	depends on HWMON && SPI_MASTER && EXPERIMENTAL
+	help
+	  If you say yes here you get support for the National Semiconductor
+	  LM70 digital temperature sensor chip.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called lm70.
+
 config SENSORS_LM75
 	tristate "National Semiconductor LM75 and compatibles"
 	depends on HWMON && I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5092999..3141584 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
+obj-$(CONFIG_SENSORS_LM70)	+= lm70.o
 obj-$(CONFIG_SENSORS_LM75)	+= lm75.o
 obj-$(CONFIG_SENSORS_LM77)	+= lm77.o
 obj-$(CONFIG_SENSORS_LM78)	+= lm78.o
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
new file mode 100644
index 0000000..6ba8473
--- /dev/null
+++ b/drivers/hwmon/lm70.c
@@ -0,0 +1,165 @@
+/*
+ * lm70.c
+ *
+ * The LM70 is a temperature sensor chip from National Semiconductor (NS).
+ * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
+ *
+ * The LM70 communicates with a host processor via an SPI/Microwire Bus
+ * interface. The complete datasheet is available at National's website
+ * here:
+ * http://www.national.com/pf/LM/LM70.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/spi/spi.h>
+#include <asm/semaphore.h>
+
+#define DRVNAME		"lm70"
+
+struct lm70 {
+	struct class_device *cdev;
+	struct semaphore sem;
+};
+
+/* sysfs hook function */
+static ssize_t lm70_sense_temp(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int status, val;
+	u8 rxbuf[2];
+	s16 raw=0;
+	struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
+
+	if (down_interruptible(&p_lm70->sem))
+		return -ERESTARTSYS;
+
+	/*
+	 * spi_read() requires a DMA-safe buffer; so we use
+	 * spi_write_then_read(), transmitting 0 bytes.
+	 */
+	status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2);
+	if (status < 0) {
+		printk(KERN_WARNING
+		"spi_write_then_read failed with status %d\n", status);
+		goto out;
+	}
+	dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]);
+
+	raw = (rxbuf[1] << 8) + rxbuf[0];
+	dev_dbg(dev, "raw=0x%x\n", raw);
+
+	/*
+	 * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's
+	 * complement value. Only the MSB 11 bits (1 sign + 10 temperature
+	 * bits) are meaningful; the LSB 5 bits are to be discarded.
+	 * See the datasheet.
+	 *
+	 * Further, each bit represents 0.25 degrees Celsius; so, multiply
+	 * by 0.25. Also multiply by 1000 to represent in millidegrees
+	 * Celsius.
+	 * So it's equivalent to multiplying by 0.25 * 1000 = 250.
+	 */
+	val = ((int)raw/32) * 250;
+	status = sprintf(buf, "%+d\n", val); /* millidegrees Celsius */
+out:
+	up(&p_lm70->sem);
+	return status;
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
+
+/*----------------------------------------------------------------------*/
+
+static int __devinit lm70_probe(struct spi_device *spi)
+{
+	struct lm70 *p_lm70;
+	int status;
+
+	p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL);
+	if (!p_lm70)
+		return -ENOMEM;
+
+	init_MUTEX(&p_lm70->sem);
+
+	/* sysfs hook */
+	p_lm70->cdev = hwmon_device_register(&spi->dev);
+	if (IS_ERR(p_lm70->cdev)) {
+		dev_dbg(&spi->dev, "hwmon_device_register failed.\n");
+		status = PTR_ERR(p_lm70->cdev);
+		goto out_dev_reg_failed;
+	}
+	dev_set_drvdata(&spi->dev, p_lm70);
+
+	if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input))) {
+		dev_dbg(&spi->dev, "device_create_file failure.\n");
+		goto out_dev_create_file_failed;
+	}
+
+	return 0;
+
+out_dev_create_file_failed:
+	hwmon_device_unregister(p_lm70->cdev);
+out_dev_reg_failed:
+	dev_set_drvdata(&spi->dev, NULL);
+	kfree(p_lm70);
+	return status;
+}
+
+static int __exit lm70_remove(struct spi_device *spi)
+{
+	struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
+
+	device_remove_file(&spi->dev, &dev_attr_temp1_input);
+	hwmon_device_unregister(p_lm70->cdev);
+	dev_set_drvdata(&spi->dev, NULL);
+	kfree(p_lm70);
+
+	return 0;
+}
+
+static struct spi_driver lm70_driver = {
+	.driver = {
+		.name	= "lm70",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= lm70_probe,
+	.remove	= __devexit_p(lm70_remove),
+};
+
+static int __init init_lm70(void)
+{
+	return spi_register_driver(&lm70_driver);
+}
+
+static void __exit cleanup_lm70(void)
+{
+	spi_unregister_driver(&lm70_driver);
+}
+
+module_init(init_lm70);
+module_exit(cleanup_lm70);
+
+MODULE_AUTHOR("Kaiwan N Billimoria");
+MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver");
+MODULE_LICENSE("GPL");
-- 
cgit v1.1


From 6af586dc58820d052aa538abef4d4d15c2a9e33e Mon Sep 17 00:00:00 2001
From: Rudolf Marek <r.marek@sh.cvut.cz>
Date: Mon, 12 Jun 2006 22:00:53 +0200
Subject: [PATCH] hwmon-vid: Add support for Intel Core and Conroe

This patch adds support for two new VID codes, supporting Intel mobile Core
processors and new Conroe based platforms.

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 | 35 ++++++++++++++++++++++++++++-------
 1 file changed, 28 insertions(+), 7 deletions(-)

(limited to 'drivers/hwmon')

diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c
index b0fd267..a6764ff 100644
--- a/drivers/hwmon/hwmon-vid.c
+++ b/drivers/hwmon/hwmon-vid.c
@@ -58,11 +58,20 @@
     doesn't seem to be any named specification for these. The conversion
     tables are detailed directly in the various Pentium M datasheets:
     http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm
+
+    The 14 specification corresponds to Intel Core series. There
+    doesn't seem to be any named specification for these. The conversion
+    tables are detailed directly in the various Pentium Core datasheets:
+    http://www.intel.com/design/mobile/datashts/309221.htm
+
+    The 110 (VRM 11) specification corresponds to Intel Conroe based series.
+    http://www.intel.com/design/processor/applnots/313214.htm
 */
 
 /* vrm is the VRM/VRD document version multiplied by 10.
-   val is the 4-, 5- or 6-bit VID code.
-   Returned value is in mV to avoid floating point in the kernel. */
+   val is the 4-bit or more VID code.
+   Returned value is in mV to avoid floating point in the kernel.
+   Some VID have some bits in uV scale, this is rounded to mV */
 int vid_from_reg(int val, u8 vrm)
 {
 	int vid;
@@ -70,18 +79,24 @@ int vid_from_reg(int val, u8 vrm)
 	switch(vrm) {
 
 	case 100:               /* VRD 10.0 */
+		/* compute in uV, round to mV */
 		val &= 0x3f;
 		if((val & 0x1f) == 0x1f)
 			return 0;
 		if((val & 0x1f) <= 0x09 || val == 0x0a)
-			vid = 10875 - (val & 0x1f) * 250;
+			vid = 1087500 - (val & 0x1f) * 25000;
 		else
-			vid = 18625 - (val & 0x1f) * 250;
+			vid = 1862500 - (val & 0x1f) * 25000;
 		if(val & 0x20)
-			vid -= 125;
-		vid /= 10;      /* only return 3 dec. places for now */
-		return vid;
+			vid -= 12500;
+		return((vid + 500) / 1000);
 
+	case 110:		/* Intel Conroe */
+				/* compute in uV, round to mV */
+		val &= 0xff;
+		if(((val & 0x7e) == 0xfe) || (!(val & 0x7e)))
+			return 0;
+		return((1600000 - (val - 2) * 6250 + 500) / 1000);
 	case 24:                /* Opteron processor */
 		val &= 0x1f;
 		return(val == 0x1f ? 0 : 1550 - val * 25);
@@ -113,6 +128,10 @@ int vid_from_reg(int val, u8 vrm)
 	case 13:
 		val &= 0x3f;
 		return(1708 - val * 16);
+	case 14:		/* Intel Core */
+				/* compute in uV, round to mV */
+		val &= 0x7f;
+		return(val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000);
 	default:		/* report 0 for unknown */
 		printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n");
 		return 0;
@@ -145,6 +164,8 @@ static struct vrm_model vrm_models[] = {
 	{X86_VENDOR_INTEL, 0x6, 0x9, ANY, 13},		/* Pentium M (130 nm) */
 	{X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85},		/* Tualatin */
 	{X86_VENDOR_INTEL, 0x6, 0xD, ANY, 13},		/* Pentium M (90 nm) */
+	{X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14},		/* Intel Core (65 nm) */
+	{X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110},		/* Intel Conroe */
 	{X86_VENDOR_INTEL, 0x6, ANY, ANY, 82},		/* any P6 */
 	{X86_VENDOR_INTEL, 0x7, ANY, ANY, 0},		/* Itanium */
 	{X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90},		/* P4 */
-- 
cgit v1.1