All of lore.kernel.org
 help / color / mirror / Atom feed
* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
@ 2006-12-21 21:13 David Hubbard
  2006-12-22 14:58 ` Jean Delvare
                   ` (29 more replies)
  0 siblings, 30 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-21 21:13 UTC (permalink / raw)
  To: lm-sensors

Hello Rudolf, Jean,

This patch applies cleanly to linux-2.6.20-rc1, adding support for the
w83627dhg. Whoever is least busy, please review. :-)

David
-------------- next part --------------
This patch adds support for the w83627dhg chip.
Signed-off-by: David Hubbard <david.c.hubbard at gmail.com>

--- old/drivers/hwmon/w83627ehf.c	2006-12-13 17:14:23.000000000 -0800
+++ new/drivers/hwmon/w83627ehf.c	2006-12-21 13:53:34.000000000 -0800
@@ -32,8 +32,10 @@
 
     Supports the following chips:
 
-    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
-    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
+    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
+    w83627ehf   10      5       4       3      0x8853 0x88    0x5ca3
+                                               0x8863 0xa1
+    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
 */
 
 #include <linux/module.h>
@@ -55,8 +57,18 @@
  * Super-I/O constants and functions
  */
 
+/*
+ * The three following globals are initialized in w83627ehf_find(), before
+ * the i2c-isa device is created. Otherwise, they could be stored in
+ * w83627ehf_data. This is ugly, but necessary. and when the driver is next
+ * updated to become a platform driver, the globals will disappear.
+ */
 static int REG;		/* The register to read/write */
 static int VAL;		/* The value to read/write */
+/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
+ * value is also used in w83627ehf_detect() to export a device name in sysfs
+ * (e.g. w83627ehf or w83627dhg) */
+static int w83627ehf_num_in;
 
 #define W83627EHF_LD_HWM	0x0b
 
@@ -65,8 +77,10 @@
 #define SIO_REG_ENABLE		0x30	/* Logical device enable */
 #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
 
-#define SIO_W83627EHF_ID	0x8840
-#define SIO_ID_MASK		0xFFC0
+#define SIO_W83627EHF_ID	0x8850
+#define SIO_W83627EHG_ID	0x8860
+#define SIO_W83627DHG_ID	0xa020
+#define SIO_ID_MASK		0xFFF0
 
 static inline void
 superio_outb(int reg, int val)
@@ -115,8 +129,12 @@
 
 #define W83627EHF_REG_BANK		0x4E
 #define W83627EHF_REG_CONFIG		0x40
-#define W83627EHF_REG_CHIP_ID		0x49
+
+/* Not currently used. 0x4f does not make sense.
+ * REG_MAN_ID is 0x5ca3 for all supported chips.
+ * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model. */
 #define W83627EHF_REG_MAN_ID		0x4F
+#define W83627EHF_REG_CHIP_ID		0x58
 
 static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
 static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@@ -429,7 +447,7 @@
 		}
 
 		/* Measured voltages and limits */
-		for (i = 0; i < 10; i++) {
+		for (i = 0; i < w83627ehf_num_in; i++) {
 			data->in[i] = w83627ehf_read_value(client,
 				      W83627EHF_REG_IN(i));
 			data->in_min[i] = w83627ehf_read_value(client,
@@ -1121,7 +1139,7 @@
 		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
 		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < w83627ehf_num_in; i++) {
 		device_remove_file(dev, &sda_in_input[i].dev_attr);
 		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
 		device_remove_file(dev, &sda_in_min[i].dev_attr);
@@ -1196,7 +1214,11 @@
 	client->flags = 0;
 	dev = &client->dev;
 
-	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+	if (w83627ehf_num_in = 9)
+		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
+	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
+		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+
 	data->valid = 0;
 	mutex_init(&data->update_lock);
 
@@ -1246,7 +1268,7 @@
 				goto exit_remove;
 		}
 
-	for (i = 0; i < 10; i++)
+	for (i = 0; i < w83627ehf_num_in; i++)
 		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
 			|| (err = device_create_file(dev,
 				&sda_in_alarm[i].dev_attr))
@@ -1340,7 +1362,17 @@
 
 	val = (superio_inb(SIO_REG_DEVID) << 8)
 	    | superio_inb(SIO_REG_DEVID + 1);
-	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
+	switch (val & SIO_ID_MASK) {
+	case SIO_W83627DHG_ID:
+		w83627ehf_num_in = 9;
+		break;
+	case SIO_W83627EHF_ID:
+	case SIO_W83627EHG_ID:
+		w83627ehf_num_in = 10;
+		break;
+	default:
+		printk(KERN_WARNING "w83627ehf: unknown ID: 0x%04x "
+			"(is this really a w83627?)\n", val);
 		superio_exit();
 		return -ENODEV;
 	}
--- old/Documentation/hwmon/w83627ehf	2006-12-13 17:14:23.000000000 -0800
+++ new/Documentation/hwmon/w83627ehf	2006-12-10 16:23:42.000000000 -0800
@@ -2,26 +2,29 @@
 =========== 
 Supported chips:
-  * Winbond W83627EHF/EHG (ISA access ONLY)
+  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
     Prefix: 'w83627ehf'
     Addresses scanned: ISA address retrieved from Super I/O registers
-    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+    Datasheet:
+        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+	DHG datasheet confidential.
 
 Authors:
         Jean Delvare <khali at linux-fr.org>
         Yuan Mu (Winbond)
         Rudolf Marek <r.marek at assembler.cz>
+        David Hubbard <david.c.hubbard at gmail.com>
 
 Description
 -----------
 
-This driver implements support for the Winbond W83627EHF and W83627EHG
-super I/O chips. We will refer to them collectively as Winbond chips.
+This driver implements support for the Winbond W83627EHF, W83627EHG, and
+W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
 
 The chips implement three temperature sensors, five fan rotation
-speed sensors, ten analog voltage sensors, alarms with beep warnings (control
-unimplemented), and some automatic fan regulation strategies (plus manual
-fan control mode).
+speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
+with beep warnings (control unimplemented), and some automatic fan regulation
+strategies (plus manual fan control mode).
 
 Temperatures are measured in degrees Celsius and measurement resolution is 1
 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@@ -55,6 +58,9 @@
 /sys files
 ----------
 
+name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
+       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg."
+
 pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
 	   0 (stop) to 255 (full)
 
@@ -83,3 +89,36 @@
 
 Note: last two functions are influenced by other control bits, not yet exported
       by the driver, so a change might not have any effect.
+
+Implementation Details
+----------------------
+Future driver development should bear in mind that the following registers have
+different functions on the 627EHF and the 627DHG. Some registers also have
+different power-on default values, but BIOS should already be loading
+appropriate defaults. Note that bank selection must be performed as is currently
+done in the driver for all register addresses.
+
+0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
+0x4a:  not completely documented for the EHF and the DHG documentation assigns
+       different behavior to bits 7 and 6, including extending the temperature
+       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
+       will reveal whether they are compatible or not.
+
+0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
+0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
+       critical temperature protection
+0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
+0x552: only on EHF, vin4
+0x558: only on EHF, vin4 high limit
+0x559: only on EHF, vin4 low limit
+0x6b:  only on DHG, SYS fan critical temperature
+0x6c:  only on DHG, CPU fan0 critical temperature
+0x6d:  only on DHG, AUX fan critical temperature
+0x6e:  only on DHG, CPU fan1 critical temperature
+
+0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
+       Register" for the DHG
+
+The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
+the ICH8 southbridge gets that data via PECI from the DHG, so that the
+southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
@ 2006-12-22 14:58 ` Jean Delvare
  2006-12-25  4:58 ` David Hubbard
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Jean Delvare @ 2006-12-22 14:58 UTC (permalink / raw)
  To: lm-sensors

Hi David,

On Thu, 21 Dec 2006 13:13:02 -0800, David Hubbard wrote:
> This patch applies cleanly to linux-2.6.20-rc1, adding support for the
> w83627dhg. Whoever is least busy, please review. :-)

Here you go. Functionally, it looks correct to me, I only have comments
on implementation choices and details.

> --- old/drivers/hwmon/w83627ehf.c	2006-12-13 17:14:23.000000000 -0800
> +++ new/drivers/hwmon/w83627ehf.c	2006-12-21 13:53:34.000000000 -0800
> @@ -32,8 +32,10 @@
>  
>      Supports the following chips:
>  
> -    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
> -    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
> +    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
> +    w83627ehf   10      5       4       3      0x8853 0x88    0x5ca3
> +                                               0x8863 0xa1
> +    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
>  */

The 4 LSB of the Super-I/O chip ID (4 LSB of LPC register 0x21) are
subject to change in the future, which is why we mask them out when
checking for device ID.

>  
>  #include <linux/module.h>
> @@ -55,8 +57,18 @@
>   * Super-I/O constants and functions
>   */
>  
> +/*
> + * The three following globals are initialized in w83627ehf_find(), before
> + * the i2c-isa device is created. Otherwise, they could be stored in
> + * w83627ehf_data. This is ugly, but necessary. and when the driver is next
> + * updated to become a platform driver, the globals will disappear.
> + */
>  static int REG;		/* The register to read/write */
>  static int VAL;		/* The value to read/write */
> +/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
> + * value is also used in w83627ehf_detect() to export a device name in sysfs
> + * (e.g. w83627ehf or w83627dhg) */
> +static int w83627ehf_num_in;

Strange choice. You store one specific difference between both chips
first, then deduce the chip name again based on this difference. A
saner approach would be to store the chip id (or kind) as a global, and
then in w83627ehf_detect(), use the value to set the number of voltage
inputs as a data structure member. It would be much more flexible that
the current approach. This will also make your job easier when
converting the driver to a platform driver.

You can take a look at the it87 driver, it does exactly this.

>  
>  #define W83627EHF_LD_HWM	0x0b
>  
> @@ -65,8 +77,10 @@
>  #define SIO_REG_ENABLE		0x30	/* Logical device enable */
>  #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
>  
> -#define SIO_W83627EHF_ID	0x8840
> -#define SIO_ID_MASK		0xFFC0
> +#define SIO_W83627EHF_ID	0x8850
> +#define SIO_W83627EHG_ID	0x8860
> +#define SIO_W83627DHG_ID	0xa020
> +#define SIO_ID_MASK		0xFFF0
>  
>  static inline void
>  superio_outb(int reg, int val)
> @@ -115,8 +129,12 @@
>  
>  #define W83627EHF_REG_BANK		0x4E
>  #define W83627EHF_REG_CONFIG		0x40
> -#define W83627EHF_REG_CHIP_ID		0x49

Good catch, I had never noticed that this register definition was bogus.

> +
> +/* Not currently used. 0x4f does not make sense.

How that?

> + * REG_MAN_ID is 0x5ca3 for all supported chips.
> + * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model. */
>  #define W83627EHF_REG_MAN_ID		0x4F
> +#define W83627EHF_REG_CHIP_ID		0x58

Why define them at all if we don't use them? Unused defines make
preprocessing more expensive.

>  
>  static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
>  static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
> @@ -429,7 +447,7 @@
>  		}
>  
>  		/* Measured voltages and limits */
> -		for (i = 0; i < 10; i++) {
> +		for (i = 0; i < w83627ehf_num_in; i++) {
>  			data->in[i] = w83627ehf_read_value(client,
>  				      W83627EHF_REG_IN(i));
>  			data->in_min[i] = w83627ehf_read_value(client,
> @@ -1121,7 +1139,7 @@
>  		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
>  	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
>  		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
> -	for (i = 0; i < 10; i++) {
> +	for (i = 0; i < w83627ehf_num_in; i++) {
>  		device_remove_file(dev, &sda_in_input[i].dev_attr);
>  		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
>  		device_remove_file(dev, &sda_in_min[i].dev_attr);
> @@ -1196,7 +1214,11 @@
>  	client->flags = 0;
>  	dev = &client->dev;
>  
> -	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
> +	if (w83627ehf_num_in = 9)
> +		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
> +	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
> +		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
> +
>  	data->valid = 0;
>  	mutex_init(&data->update_lock);
>  
> @@ -1246,7 +1268,7 @@
>  				goto exit_remove;
>  		}
>  
> -	for (i = 0; i < 10; i++)
> +	for (i = 0; i < w83627ehf_num_in; i++)
>  		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
>  			|| (err = device_create_file(dev,
>  				&sda_in_alarm[i].dev_attr))
> @@ -1340,7 +1362,17 @@
>  
>  	val = (superio_inb(SIO_REG_DEVID) << 8)
>  	    | superio_inb(SIO_REG_DEVID + 1);
> -	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
> +	switch (val & SIO_ID_MASK) {
> +	case SIO_W83627DHG_ID:
> +		w83627ehf_num_in = 9;
> +		break;
> +	case SIO_W83627EHF_ID:
> +	case SIO_W83627EHG_ID:
> +		w83627ehf_num_in = 10;
> +		break;
> +	default:
> +		printk(KERN_WARNING "w83627ehf: unknown ID: 0x%04x "
> +			"(is this really a w83627?)\n", val);

It could be one of the other W83627 chips (HF and THF), which are not
(and will never be) supported by this driver, so this comment is a bit
misleading. Maybe just "Unsupported chip"?

>  		superio_exit();
>  		return -ENODEV;
>  	}
> --- old/Documentation/hwmon/w83627ehf	2006-12-13 17:14:23.000000000 -0800
> +++ new/Documentation/hwmon/w83627ehf	2006-12-10 16:23:42.000000000 -0800
> @@ -2,26 +2,29 @@
>  ===========>  
>  Supported chips:
> -  * Winbond W83627EHF/EHG (ISA access ONLY)
> +  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
>      Prefix: 'w83627ehf'
>      Addresses scanned: ISA address retrieved from Super I/O registers
> -    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
> +    Datasheet:
> +        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
> +	DHG datasheet confidential.

Broken alignment.

>  
>  Authors:
>          Jean Delvare <khali at linux-fr.org>
>          Yuan Mu (Winbond)
>          Rudolf Marek <r.marek at assembler.cz>
> +        David Hubbard <david.c.hubbard at gmail.com>
>  
>  Description
>  -----------
>  
> -This driver implements support for the Winbond W83627EHF and W83627EHG
> -super I/O chips. We will refer to them collectively as Winbond chips.
> +This driver implements support for the Winbond W83627EHF, W83627EHG, and
> +W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
>  
>  The chips implement three temperature sensors, five fan rotation
> -speed sensors, ten analog voltage sensors, alarms with beep warnings (control
> -unimplemented), and some automatic fan regulation strategies (plus manual
> -fan control mode).
> +speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
> +with beep warnings (control unimplemented), and some automatic fan regulation
> +strategies (plus manual fan control mode).
>  
>  Temperatures are measured in degrees Celsius and measurement resolution is 1
>  degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
> @@ -55,6 +58,9 @@
>  /sys files
>  ----------
>  
> +name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
> +       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg."

Maybe move the final dot outside of the quotes to avoid unnecessaryt
confusion.

> +
>  pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
>  	   0 (stop) to 255 (full)
>  
> @@ -83,3 +89,36 @@
>  
>  Note: last two functions are influenced by other control bits, not yet exported
>        by the driver, so a change might not have any effect.
> +
> +Implementation Details
> +----------------------
> +Future driver development should bear in mind that the following registers have
> +different functions on the 627EHF and the 627DHG. Some registers also have
> +different power-on default values, but BIOS should already be loading
> +appropriate defaults. Note that bank selection must be performed as is currently
> +done in the driver for all register addresses.
> +
> +0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
> +0x4a:  not completely documented for the EHF and the DHG documentation assigns
> +       different behavior to bits 7 and 6, including extending the temperature
> +       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
> +       will reveal whether they are compatible or not.
> +
> +0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
> +0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
> +       critical temperature protection
> +0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
> +0x552: only on EHF, vin4
> +0x558: only on EHF, vin4 high limit
> +0x559: only on EHF, vin4 low limit
> +0x6b:  only on DHG, SYS fan critical temperature
> +0x6c:  only on DHG, CPU fan0 critical temperature
> +0x6d:  only on DHG, AUX fan critical temperature
> +0x6e:  only on DHG, CPU fan1 critical temperature
> +
> +0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
> +       Register" for the DHG

This last paragraph isn't particularly helpful, "test" and "reserved"
really mean the same. 

> +
> +The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
> +the ICH8 southbridge gets that data via PECI from the DHG, so that the
> +southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.

Thanks,
-- 
Jean Delvare


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
  2006-12-22 14:58 ` Jean Delvare
@ 2006-12-25  4:58 ` David Hubbard
  2006-12-25 18:23 ` Jean Delvare
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-25  4:58 UTC (permalink / raw)
  To: lm-sensors

Hi Jean,

> The 4 LSB of the Super-I/O chip ID (4 LSB of LPC register 0x21) are
> subject to change in the future, which is why we mask them out when
> checking for device ID.

I updated the comment and replaced the 4 LSB with zeros. There isn't
room to note that the 4 LSB are ignored in the comment, but the
implementation uses SIO_ID_MASK = 0xFFF0, so it should be correct. In
w83627ehf_find(), I thought about masking val when printing the
KERN_WARNING, but I prefer more information rather than less
information. Also, I think the it is more readable to mask val in
switch (val & SIO_ID_MASK).

> Strange choice. You store one specific difference between both chips
> first, then deduce the chip name again based on this difference. A
> saner approach would be to store the chip id (or kind) as a global, and
> then in w83627ehf_detect(), use the value to set the number of voltage
> inputs as a data structure member. It would be much more flexible that
> the current approach. This will also make your job easier when
> converting the driver to a platform driver.

I agree. I would prefer to store specific values in w83627ehf_data and
detect the chips only once. However, I went with a compromise and so
in w83627ehf_find() when I detect the 627ehf, 627ehg, or 627dhg, I
store one value (w83627ehf_num_in) when I should be storing both
num_in and name. I consider the creation of a global variable a poor
solution, and I will change the implementation as quickly as possible.
I don't want to detect things twice (once in w83627ehf_find() and
later in w83627ehf_detect()).

> You can take a look at the it87 driver, it does exactly this.

I will, in a future patch.

> > +
> > +/* Not currently used. 0x4f does not make sense.
>
> How that?
>
> > + * REG_MAN_ID is 0x5ca3 for all supported chips.
> > + * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model. */
> >  #define W83627EHF_REG_MAN_ID         0x4F
> > +#define W83627EHF_REG_CHIP_ID                0x58
>
> Why define them at all if we don't use them? Unused defines make
> preprocessing more expensive.

In this patch I have changed the defines to comments. When I said
"0x4f does not make sense" I don't know why I typed 0x4f there. I
meant 0x49. The patch deletes the #define W83627EHF_REG_CHIP_ID 0x49.
The correct port is 0x4f.

> It could be one of the other W83627 chips (HF and THF), which are not
> (and will never be) supported by this driver, so this comment is a bit
> misleading. Maybe just "Unsupported chip"?

Good catch. Message updated.

> Broken alignment.

Fixed.

> Maybe move the final dot outside of the quotes to avoid unnecessaryt
> confusion.

I just deleted the period.

> This last paragraph isn't particularly helpful, "test" and "reserved"
> really mean the same.

Yes, but I feel the information is still worth adding, for
completeness. (It could be argued that all the information in this
section isn't particularly helpful; one should just read the driver. I
would argue that it is still worth having.) I have left it in the
patch, but if you feel strongly about it, I'll delete it.

Thanks,
David
-------------- next part --------------
This patch adds support for the w83627dhg chip.
Signed-off-by: David Hubbard <david.c.hubbard at gmail.com>

--- old/drivers/hwmon/w83627ehf.c	2006-12-21 14:04:19.000000000 -0800
+++ new/drivers/hwmon/w83627ehf.c	2006-12-24 21:53:54.000000000 -0800
@@ -32,8 +32,10 @@
 
     Supports the following chips:
 
-    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
-    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
+    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
+    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
+                                               0x8860 0xa1
+    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
 */
 
 #include <linux/module.h>
@@ -55,8 +57,18 @@
  * Super-I/O constants and functions
  */
 
+/*
+ * The three following globals are initialized in w83627ehf_find(), before
+ * the i2c-isa device is created. Otherwise, they could be stored in
+ * w83627ehf_data. This is ugly, but necessary. and when the driver is next
+ * updated to become a platform driver, the globals will disappear.
+ */
 static int REG;		/* The register to read/write */
 static int VAL;		/* The value to read/write */
+/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
+ * value is also used in w83627ehf_detect() to export a device name in sysfs
+ * (e.g. w83627ehf or w83627dhg) */
+static int w83627ehf_num_in;
 
 #define W83627EHF_LD_HWM	0x0b
 
@@ -65,8 +77,10 @@
 #define SIO_REG_ENABLE		0x30	/* Logical device enable */
 #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
 
-#define SIO_W83627EHF_ID	0x8840
-#define SIO_ID_MASK		0xFFC0
+#define SIO_W83627EHF_ID	0x8850
+#define SIO_W83627EHG_ID	0x8860
+#define SIO_W83627DHG_ID	0xa020
+#define SIO_ID_MASK		0xFFF0
 
 static inline void
 superio_outb(int reg, int val)
@@ -115,8 +129,12 @@
 
 #define W83627EHF_REG_BANK		0x4E
 #define W83627EHF_REG_CONFIG		0x40
-#define W83627EHF_REG_CHIP_ID		0x49
-#define W83627EHF_REG_MAN_ID		0x4F
+
+/* Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58 */
 
 static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
 static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@@ -429,7 +447,7 @@
 		}
 
 		/* Measured voltages and limits */
-		for (i = 0; i < 10; i++) {
+		for (i = 0; i < w83627ehf_num_in; i++) {
 			data->in[i] = w83627ehf_read_value(client,
 				      W83627EHF_REG_IN(i));
 			data->in_min[i] = w83627ehf_read_value(client,
@@ -1121,7 +1139,7 @@
 		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
 		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < w83627ehf_num_in; i++) {
 		device_remove_file(dev, &sda_in_input[i].dev_attr);
 		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
 		device_remove_file(dev, &sda_in_min[i].dev_attr);
@@ -1196,7 +1214,11 @@
 	client->flags = 0;
 	dev = &client->dev;
 
-	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+	if (w83627ehf_num_in = 9)
+		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
+	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
+		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+
 	data->valid = 0;
 	mutex_init(&data->update_lock);
 
@@ -1246,7 +1268,7 @@
 				goto exit_remove;
 		}
 
-	for (i = 0; i < 10; i++)
+	for (i = 0; i < w83627ehf_num_in; i++)
 		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
 			|| (err = device_create_file(dev,
 				&sda_in_alarm[i].dev_attr))
@@ -1340,7 +1362,17 @@
 
 	val = (superio_inb(SIO_REG_DEVID) << 8)
 	    | superio_inb(SIO_REG_DEVID + 1);
-	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
+	switch (val & SIO_ID_MASK) {
+	case SIO_W83627DHG_ID:
+		w83627ehf_num_in = 9;
+		break;
+	case SIO_W83627EHF_ID:
+	case SIO_W83627EHG_ID:
+		w83627ehf_num_in = 10;
+		break;
+	default:
+		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
+			val);
 		superio_exit();
 		return -ENODEV;
 	}
--- old/Documentation/hwmon/w83627ehf	2006-12-21 14:04:19.000000000 -0800
+++ new/Documentation/hwmon/w83627ehf	2006-12-24 21:55:46.000000000 -0800
@@ -2,26 +2,29 @@
 =========== 
 Supported chips:
-  * Winbond W83627EHF/EHG (ISA access ONLY)
+  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
     Prefix: 'w83627ehf'
     Addresses scanned: ISA address retrieved from Super I/O registers
-    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+    Datasheet:
+        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+        DHG datasheet confidential.
 
 Authors:
         Jean Delvare <khali at linux-fr.org>
         Yuan Mu (Winbond)
         Rudolf Marek <r.marek at assembler.cz>
+        David Hubbard <david.c.hubbard at gmail.com>
 
 Description
 -----------
 
-This driver implements support for the Winbond W83627EHF and W83627EHG
-super I/O chips. We will refer to them collectively as Winbond chips.
+This driver implements support for the Winbond W83627EHF, W83627EHG, and
+W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
 
 The chips implement three temperature sensors, five fan rotation
-speed sensors, ten analog voltage sensors, alarms with beep warnings (control
-unimplemented), and some automatic fan regulation strategies (plus manual
-fan control mode).
+speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
+with beep warnings (control unimplemented), and some automatic fan regulation
+strategies (plus manual fan control mode).
 
 Temperatures are measured in degrees Celsius and measurement resolution is 1
 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@@ -55,6 +58,9 @@
 /sys files
 ----------
 
+name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
+       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg"
+
 pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
 	   0 (stop) to 255 (full)
 
@@ -83,3 +89,36 @@
 
 Note: last two functions are influenced by other control bits, not yet exported
       by the driver, so a change might not have any effect.
+
+Implementation Details
+----------------------
+Future driver development should bear in mind that the following registers have
+different functions on the 627EHF and the 627DHG. Some registers also have
+different power-on default values, but BIOS should already be loading
+appropriate defaults. Note that bank selection must be performed as is currently
+done in the driver for all register addresses.
+
+0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
+0x4a:  not completely documented for the EHF and the DHG documentation assigns
+       different behavior to bits 7 and 6, including extending the temperature
+       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
+       will reveal whether they are compatible or not.
+
+0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
+0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
+       critical temperature protection
+0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
+0x552: only on EHF, vin4
+0x558: only on EHF, vin4 high limit
+0x559: only on EHF, vin4 low limit
+0x6b:  only on DHG, SYS fan critical temperature
+0x6c:  only on DHG, CPU fan0 critical temperature
+0x6d:  only on DHG, AUX fan critical temperature
+0x6e:  only on DHG, CPU fan1 critical temperature
+
+0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
+       Register" for the DHG
+
+The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
+the ICH8 southbridge gets that data via PECI from the DHG, so that the
+southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
  2006-12-22 14:58 ` Jean Delvare
  2006-12-25  4:58 ` David Hubbard
@ 2006-12-25 18:23 ` Jean Delvare
  2006-12-26  8:02 ` David Hubbard
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Jean Delvare @ 2006-12-25 18:23 UTC (permalink / raw)
  To: lm-sensors

Hi David,

On Sun, 24 Dec 2006 20:58:48 -0800, David Hubbard wrote:
> > The 4 LSB of the Super-I/O chip ID (4 LSB of LPC register 0x21) are
> > subject to change in the future, which is why we mask them out when
> > checking for device ID.
> 
> I updated the comment and replaced the 4 LSB with zeros. There isn't
> room to note that the 4 LSB are ignored in the comment, but the
> implementation uses SIO_ID_MASK = 0xFFF0, so it should be correct. In
> w83627ehf_find(), I thought about masking val when printing the
> KERN_WARNING, but I prefer more information rather than less
> information. Also, I think the it is more readable to mask val in
> switch (val & SIO_ID_MASK).

Agreed, I'm fine with your implementation.

> > Strange choice. You store one specific difference between both chips
> > first, then deduce the chip name again based on this difference. A
> > saner approach would be to store the chip id (or kind) as a global, and
> > then in w83627ehf_detect(), use the value to set the number of voltage
> > inputs as a data structure member. It would be much more flexible that
> > the current approach. This will also make your job easier when
> > converting the driver to a platform driver.
> 
> I agree. I would prefer to store specific values in w83627ehf_data and
> detect the chips only once. However, I went with a compromise and so
> in w83627ehf_find() when I detect the 627ehf, 627ehg, or 627dhg, I
> store one value (w83627ehf_num_in) when I should be storing both
> num_in and name. I consider the creation of a global variable a poor
> solution, and I will change the implementation as quickly as possible.
> I don't want to detect things twice (once in w83627ehf_find() and
> later in w83627ehf_detect()).

Well, you do detect things twice in the current implementation too.
There's no way around it as long as the driver is based on i2c-isa.

> > You can take a look at the it87 driver, it does exactly this.
> 
> I will, in a future patch.

OK, I'm looking forward to it :) If you do it at the same time you
convert the driver to a platform driver, f71805f will be a better
example though.

> > This last paragraph isn't particularly helpful, "test" and "reserved"
> > really mean the same.
> 
> Yes, but I feel the information is still worth adding, for
> completeness. (It could be argued that all the information in this
> section isn't particularly helpful; one should just read the driver. I
> would argue that it is still worth having.) I have left it in the
> patch, but if you feel strongly about it, I'll delete it.

No, I don't. It's your patch after all ;)

So, your patch is applied, and will be in the next -mm kernel, and will
then go in kernel 2.6.21:
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-hwmon/hwmon-w83627ehf-add-w83627dhg-support.patch

Thanks for your contribution.

-- 
Jean Delvare


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (2 preceding siblings ...)
  2006-12-25 18:23 ` Jean Delvare
@ 2006-12-26  8:02 ` David Hubbard
  2006-12-26 10:31 ` Hamlet
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-26  8:02 UTC (permalink / raw)
  To: lm-sensors

Hello everyone. This email is to let you know that W83627DHG support
has been added to the kernel source tree (Andrew Morton's -mm branch,
which is for new drivers).

Jean Delvare wrote:
> So, your patch is applied, and will be in the next -mm kernel, and
> will then go in kernel 2.6.21:
> http://khali.linux-fr.org/devel/linux-2.6/jdelvare-hwmon/hwmon-w83627ehf-add-w83627dhg-support.patch

You are included in this email because some time in the last four
months you expressed interest in W83627DHG support. If you would like
to help test the new driver, the instructions are below. Please
remember to include lm-sensors at lm-sensors.org in any reply messages.

The patch attached to this email and at the URL above applies cleanly
to linux kernel versions 2.6.19-rc6, 2.6.20-rc1 or 2.6.20-rc2. Kernel
tarballs are available at:
http://www.kernel.org/pub/linux/kernel/v2.6/testing/

In addition, the driver may also work for you on 2.6.17 and 2.6.18
kernels by downloading w83627ehf.c.txt, the second attachment to this
email. Then save your original driver and copy it into the kernel
source tree. For example:

# cd /usr/src/linux/drivers/hwmon
hwmon # cp w83627ehf.c w83627ehf.c.orig
hwmon # cp w83627ehf.c.txt w83627ehf.c
hwmon # cd ../..
linux # make && make modules_install

However, if you find any bugs, the first thing you will have to do is
upgrade your kernel to the latest version. This is not an official
backport. There is an official request open for a w83627ehf.c backport
to linux 2.4. (And I would not mind anyone who wants to jump in on
that as well.)

The third attachment is a shell script I use to do basic regression
testing of the driver. If it reports any errors on your system, please
report them! This driver should not break any functionality from
previous drivers. The more important test is continuous usage of the
driver, so please load sensors, ksensors, gkrellm, or whatever
monitoring tools you normally use, and hit reply if you see anything
unusual over the next few weeks.

You may need to download the latest version of the sensors source from
http://www.lm-sensors.org/wiki/Download#SubversionRepository in order
to have W83627DHG support in user space.

Thanks for your assistance,
David

Rudolf: Can you update ticket #2142? This should resolve it.
-------------- next part --------------
This patch adds support for the w83627dhg chip.
Signed-off-by: David Hubbard <david.c.hubbard at gmail.com>

--- old/drivers/hwmon/w83627ehf.c	2006-12-21 14:04:19.000000000 -0800
+++ new/drivers/hwmon/w83627ehf.c	2006-12-24 21:53:54.000000000 -0800
@@ -32,8 +32,10 @@
 
     Supports the following chips:
 
-    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
-    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
+    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
+    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
+                                               0x8860 0xa1
+    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
 */
 
 #include <linux/module.h>
@@ -55,8 +57,18 @@
  * Super-I/O constants and functions
  */
 
+/*
+ * The three following globals are initialized in w83627ehf_find(), before
+ * the i2c-isa device is created. Otherwise, they could be stored in
+ * w83627ehf_data. This is ugly, but necessary. and when the driver is next
+ * updated to become a platform driver, the globals will disappear.
+ */
 static int REG;		/* The register to read/write */
 static int VAL;		/* The value to read/write */
+/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
+ * value is also used in w83627ehf_detect() to export a device name in sysfs
+ * (e.g. w83627ehf or w83627dhg) */
+static int w83627ehf_num_in;
 
 #define W83627EHF_LD_HWM	0x0b
 
@@ -65,8 +77,10 @@
 #define SIO_REG_ENABLE		0x30	/* Logical device enable */
 #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
 
-#define SIO_W83627EHF_ID	0x8840
-#define SIO_ID_MASK		0xFFC0
+#define SIO_W83627EHF_ID	0x8850
+#define SIO_W83627EHG_ID	0x8860
+#define SIO_W83627DHG_ID	0xa020
+#define SIO_ID_MASK		0xFFF0
 
 static inline void
 superio_outb(int reg, int val)
@@ -115,8 +129,12 @@
 
 #define W83627EHF_REG_BANK		0x4E
 #define W83627EHF_REG_CONFIG		0x40
-#define W83627EHF_REG_CHIP_ID		0x49
-#define W83627EHF_REG_MAN_ID		0x4F
+
+/* Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58 */
 
 static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
 static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@@ -429,7 +447,7 @@
 		}
 
 		/* Measured voltages and limits */
-		for (i = 0; i < 10; i++) {
+		for (i = 0; i < w83627ehf_num_in; i++) {
 			data->in[i] = w83627ehf_read_value(client,
 				      W83627EHF_REG_IN(i));
 			data->in_min[i] = w83627ehf_read_value(client,
@@ -1121,7 +1139,7 @@
 		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
 		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < w83627ehf_num_in; i++) {
 		device_remove_file(dev, &sda_in_input[i].dev_attr);
 		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
 		device_remove_file(dev, &sda_in_min[i].dev_attr);
@@ -1196,7 +1214,11 @@
 	client->flags = 0;
 	dev = &client->dev;
 
-	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+	if (w83627ehf_num_in = 9)
+		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
+	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
+		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+
 	data->valid = 0;
 	mutex_init(&data->update_lock);
 
@@ -1246,7 +1268,7 @@
 				goto exit_remove;
 		}
 
-	for (i = 0; i < 10; i++)
+	for (i = 0; i < w83627ehf_num_in; i++)
 		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
 			|| (err = device_create_file(dev,
 				&sda_in_alarm[i].dev_attr))
@@ -1340,7 +1362,17 @@
 
 	val = (superio_inb(SIO_REG_DEVID) << 8)
 	    | superio_inb(SIO_REG_DEVID + 1);
-	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
+	switch (val & SIO_ID_MASK) {
+	case SIO_W83627DHG_ID:
+		w83627ehf_num_in = 9;
+		break;
+	case SIO_W83627EHF_ID:
+	case SIO_W83627EHG_ID:
+		w83627ehf_num_in = 10;
+		break;
+	default:
+		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
+			val);
 		superio_exit();
 		return -ENODEV;
 	}
--- old/Documentation/hwmon/w83627ehf	2006-12-21 14:04:19.000000000 -0800
+++ new/Documentation/hwmon/w83627ehf	2006-12-24 21:55:46.000000000 -0800
@@ -2,26 +2,29 @@
 =========== 
 Supported chips:
-  * Winbond W83627EHF/EHG (ISA access ONLY)
+  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
     Prefix: 'w83627ehf'
     Addresses scanned: ISA address retrieved from Super I/O registers
-    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+    Datasheet:
+        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+        DHG datasheet confidential.
 
 Authors:
         Jean Delvare <khali at linux-fr.org>
         Yuan Mu (Winbond)
         Rudolf Marek <r.marek at assembler.cz>
+        David Hubbard <david.c.hubbard at gmail.com>
 
 Description
 -----------
 
-This driver implements support for the Winbond W83627EHF and W83627EHG
-super I/O chips. We will refer to them collectively as Winbond chips.
+This driver implements support for the Winbond W83627EHF, W83627EHG, and
+W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
 
 The chips implement three temperature sensors, five fan rotation
-speed sensors, ten analog voltage sensors, alarms with beep warnings (control
-unimplemented), and some automatic fan regulation strategies (plus manual
-fan control mode).
+speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
+with beep warnings (control unimplemented), and some automatic fan regulation
+strategies (plus manual fan control mode).
 
 Temperatures are measured in degrees Celsius and measurement resolution is 1
 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@@ -55,6 +58,9 @@
 /sys files
 ----------
 
+name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
+       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg"
+
 pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
 	   0 (stop) to 255 (full)
 
@@ -83,3 +89,36 @@
 
 Note: last two functions are influenced by other control bits, not yet exported
       by the driver, so a change might not have any effect.
+
+Implementation Details
+----------------------
+Future driver development should bear in mind that the following registers have
+different functions on the 627EHF and the 627DHG. Some registers also have
+different power-on default values, but BIOS should already be loading
+appropriate defaults. Note that bank selection must be performed as is currently
+done in the driver for all register addresses.
+
+0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
+0x4a:  not completely documented for the EHF and the DHG documentation assigns
+       different behavior to bits 7 and 6, including extending the temperature
+       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
+       will reveal whether they are compatible or not.
+
+0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
+0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
+       critical temperature protection
+0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
+0x552: only on EHF, vin4
+0x558: only on EHF, vin4 high limit
+0x559: only on EHF, vin4 low limit
+0x6b:  only on DHG, SYS fan critical temperature
+0x6c:  only on DHG, CPU fan0 critical temperature
+0x6d:  only on DHG, AUX fan critical temperature
+0x6e:  only on DHG, CPU fan1 critical temperature
+
+0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
+       Register" for the DHG
+
+The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
+the ICH8 southbridge gets that data via PECI from the DHG, so that the
+southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
-------------- next part --------------
/*
    w83627ehf - Driver for the hardware monitoring functionality of
                the Winbond W83627EHF Super-I/O chip
    Copyright (C) 2005  Jean Delvare <khali at linux-fr.org>
    Copyright (C) 2006  Yuan Mu (Winbond),
                        Rudolf Marek <r.marek at assembler.cz>
                        David Hubbard <david.c.hubbard at gmail.com>

    Shamelessly ripped from the w83627hf driver
    Copyright (C) 2003  Mark Studebaker

    Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
    in testing and debugging this driver.

    This driver also supports the W83627EHG, which is the lead-free
    version of the W83627EHF.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


    Supports the following chips:

    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
                                               0x8860 0xa1
    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-isa.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <asm/io.h>
#include "lm75.h"

/* The actual ISA address is read from Super-I/O configuration space */
static unsigned short address;

/*
 * Super-I/O constants and functions
 */

/*
 * The three following globals are initialized in w83627ehf_find(), before
 * the i2c-isa device is created. Otherwise, they could be stored in
 * w83627ehf_data. This is ugly, but necessary. and when the driver is next
 * updated to become a platform driver, the globals will disappear.
 */
static int REG;		/* The register to read/write */
static int VAL;		/* The value to read/write */
/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
 * value is also used in w83627ehf_detect() to export a device name in sysfs
 * (e.g. w83627ehf or w83627dhg) */
static int w83627ehf_num_in;

#define W83627EHF_LD_HWM	0x0b

#define SIO_REG_LDSEL		0x07	/* Logical device select */
#define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
#define SIO_REG_ENABLE		0x30	/* Logical device enable */
#define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */

#define SIO_W83627EHF_ID	0x8850
#define SIO_W83627EHG_ID	0x8860
#define SIO_W83627DHG_ID	0xa020
#define SIO_ID_MASK		0xFFF0

static inline void
superio_outb(int reg, int val)
{
	outb(reg, REG);
	outb(val, VAL);
}

static inline int
superio_inb(int reg)
{
	outb(reg, REG);
	return inb(VAL);
}

static inline void
superio_select(int ld)
{
	outb(SIO_REG_LDSEL, REG);
	outb(ld, VAL);
}

static inline void
superio_enter(void)
{
	outb(0x87, REG);
	outb(0x87, REG);
}

static inline void
superio_exit(void)
{
	outb(0x02, REG);
	outb(0x02, VAL);
}

/*
 * ISA constants
 */

#define REGION_ALIGNMENT	~7
#define REGION_OFFSET		5
#define REGION_LENGTH		2
#define ADDR_REG_OFFSET		5
#define DATA_REG_OFFSET		6

#define W83627EHF_REG_BANK		0x4E
#define W83627EHF_REG_CONFIG		0x40

/* Not currently used:
 * REG_MAN_ID has the value 0x5ca3 for all supported chips.
 * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
 * REG_MAN_ID is at port 0x4f
 * REG_CHIP_ID is at port 0x58 */

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
static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };

/* Fan clock dividers are spread over the following five registers */
#define W83627EHF_REG_FANDIV1		0x47
#define W83627EHF_REG_FANDIV2		0x4B
#define W83627EHF_REG_VBAT		0x5D
#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

/* SmartFan registers */
/* DC or PWM output fan configuration */
static const u8 W83627EHF_REG_PWM_ENABLE[] = {
	0x04,			/* SYS FAN0 output mode and PWM mode */
	0x04,			/* CPU FAN0 output mode and PWM mode */
	0x12,			/* AUX FAN mode */
	0x62,			/* CPU fan1 mode */
};

static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };

/* FAN Duty Cycle, be used to control */
static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };


/* Advanced Fan control, some values are common for all fans */
static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };

/*
 * Conversions
 */

/* 1 is PWM mode, output in ms */
static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
{
	return mode ? 100 * reg : 400 * reg;
}

static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
{
	return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
						(msec + 200) / 400), 1, 255);
}

static inline unsigned int
fan_from_reg(u8 reg, unsigned int div)
{
	if (reg = 0 || reg = 255)
		return 0;
	return 1350000U / (reg * div);
}

static inline unsigned int
div_from_reg(u8 reg)
{
	return 1 << reg;
}

static inline int
temp1_from_reg(s8 reg)
{
	return reg * 1000;
}

static inline s8
temp1_to_reg(int temp, int min, int max)
{
	if (temp <= min)
		return min / 1000;
	if (temp >= max)
		return max / 1000;
	if (temp < 0)
		return (temp - 500) / 1000;
	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
 */

struct w83627ehf_data {
	struct i2c_client client;
	struct class_device *class_dev;
	struct mutex lock;

	struct mutex update_lock;
	char valid;		/* !=0 if following fields are valid */
	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];
	u8 has_fan;		/* some fan inputs can be disabled */
	s8 temp1;
	s8 temp1_max;
	s8 temp1_max_hyst;
	s16 temp[2];
	s16 temp_max[2];
	s16 temp_max_hyst[2];
	u32 alarms;

	u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
	u8 pwm_enable[4]; /* 1->manual
			     2->thermal cruise (also called SmartFan I) */
	u8 pwm[4];
	u8 target_temp[4];
	u8 tolerance[4];

	u8 fan_min_output[4]; /* minimum fan speed */
	u8 fan_stop_time[4];
};

static inline int is_word_sized(u16 reg)
{
	return (((reg & 0xff00) = 0x100
	      || (reg & 0xff00) = 0x200)
	     && ((reg & 0x00ff) = 0x50
	      || (reg & 0x00ff) = 0x53
	      || (reg & 0x00ff) = 0x55));
}

/* We assume that the default bank is 0, thus the following two functions do
   nothing for registers which live in bank 0. For others, they respectively
   set the bank register to the correct value (before the register is
   accessed), and back to 0 (afterwards). */
static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
{
	if (reg & 0xff00) {
		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
		outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
	}
}

static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
{
	if (reg & 0xff00) {
		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
		outb_p(0, client->addr + DATA_REG_OFFSET);
	}
}

static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int res, word_sized = is_word_sized(reg);

	mutex_lock(&data->lock);

	w83627ehf_set_bank(client, reg);
	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
	res = inb_p(client->addr + DATA_REG_OFFSET);
	if (word_sized) {
		outb_p((reg & 0xff) + 1,
		       client->addr + ADDR_REG_OFFSET);
		res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
	}
	w83627ehf_reset_bank(client, reg);

	mutex_unlock(&data->lock);

	return res;
}

static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int word_sized = is_word_sized(reg);

	mutex_lock(&data->lock);

	w83627ehf_set_bank(client, reg);
	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
	if (word_sized) {
		outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
		outb_p((reg & 0xff) + 1,
		       client->addr + ADDR_REG_OFFSET);
	}
	outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
	w83627ehf_reset_bank(client, reg);

	mutex_unlock(&data->lock);
	return 0;
}

/* This function assumes that the caller holds data->update_lock */
static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	u8 reg;

	switch (nr) {
	case 0:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
		    | ((data->fan_div[0] & 0x03) << 4);
		/* fan5 input control bit is write only, compute the value */
		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
		    | ((data->fan_div[0] & 0x04) << 3);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 1:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
		    | ((data->fan_div[1] & 0x03) << 6);
		/* fan5 input control bit is write only, compute the value */
		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
		    | ((data->fan_div[1] & 0x04) << 4);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 2:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
		    | ((data->fan_div[2] & 0x03) << 6);
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
		    | ((data->fan_div[2] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 3:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
		    | (data->fan_div[3] & 0x03);
		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
		    | ((data->fan_div[3] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
		break;
	case 4:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
		    | ((data->fan_div[4] & 0x03) << 3)
		    | ((data->fan_div[4] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
		break;
	}
}

static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
	int i;

	mutex_lock(&data->update_lock);

	if (time_after(jiffies, data->last_updated + HZ)
	 || !data->valid) {
		/* Fan clock dividers */
		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
		data->fan_div[0] = (i >> 4) & 0x03;
		data->fan_div[1] = (i >> 6) & 0x03;
		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
		data->fan_div[2] = (i >> 6) & 0x03;
		i = w83627ehf_read_value(client, W83627EHF_REG_VBAT);
		data->fan_div[0] |= (i >> 3) & 0x04;
		data->fan_div[1] |= (i >> 4) & 0x04;
		data->fan_div[2] |= (i >> 5) & 0x04;
		if (data->has_fan & ((1 << 3) | (1 << 4))) {
			i = w83627ehf_read_value(client, W83627EHF_REG_DIODE);
			data->fan_div[3] = i & 0x03;
			data->fan_div[4] = ((i >> 2) & 0x03)
					 | ((i >> 5) & 0x04);
		}
		if (data->has_fan & (1 << 3)) {
			i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT);
			data->fan_div[3] |= (i >> 5) & 0x04;
		}

		/* Measured voltages and limits */
		for (i = 0; i < w83627ehf_num_in; 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)))
				continue;

			data->fan[i] = w83627ehf_read_value(client,
				       W83627EHF_REG_FAN[i]);
			data->fan_min[i] = w83627ehf_read_value(client,
					   W83627EHF_REG_FAN_MIN[i]);

			/* If we failed to measure the fan speed and clock
			   divider can be increased, let's try that for next
			   time */
			if (data->fan[i] = 0xff
			 && data->fan_div[i] < 0x07) {
			 	dev_dbg(&client->dev, "Increasing fan %d "
					"clock divider from %u to %u\n",
					i, div_from_reg(data->fan_div[i]),
					div_from_reg(data->fan_div[i] + 1));
				data->fan_div[i]++;
				w83627ehf_write_fan_div(client, i);
				/* Preserve min limit if possible */
				if (data->fan_min[i] >= 2
				 && data->fan_min[i] != 255)
					w83627ehf_write_value(client,
						W83627EHF_REG_FAN_MIN[i],
						(data->fan_min[i] /= 2));
			}
		}

		for (i = 0; i < 4; i++) {
			/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
			if (i != 1) {
				pwmcfg = w83627ehf_read_value(client,
						W83627EHF_REG_PWM_ENABLE[i]);
				tolerance = w83627ehf_read_value(client,
						W83627EHF_REG_TOLERANCE[i]);
			}
			data->pwm_mode[i] 				((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
				? 0 : 1;
			data->pwm_enable[i] 					((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
						& 3) + 1;
			data->pwm[i] = w83627ehf_read_value(client,
						W83627EHF_REG_PWM[i]);
			data->fan_min_output[i] = w83627ehf_read_value(client,
						W83627EHF_REG_FAN_MIN_OUTPUT[i]);
			data->fan_stop_time[i] = w83627ehf_read_value(client,
						W83627EHF_REG_FAN_STOP_TIME[i]);
			data->target_temp[i] 				w83627ehf_read_value(client,
					W83627EHF_REG_TARGET[i]) &
					(data->pwm_mode[i] = 1 ? 0x7f : 0xff);
			data->tolerance[i] = (tolerance >> (i = 1 ? 4 : 0))
									& 0x0f;
		}

		/* Measured temperatures and limits */
		data->temp1 = w83627ehf_read_value(client,
			      W83627EHF_REG_TEMP1);
		data->temp1_max = w83627ehf_read_value(client,
				  W83627EHF_REG_TEMP1_OVER);
		data->temp1_max_hyst = w83627ehf_read_value(client,
				       W83627EHF_REG_TEMP1_HYST);
		for (i = 0; i < 2; i++) {
			data->temp[i] = w83627ehf_read_value(client,
					W83627EHF_REG_TEMP[i]);
			data->temp_max[i] = w83627ehf_read_value(client,
					    W83627EHF_REG_TEMP_OVER[i]);
			data->temp_max_hyst[i] = w83627ehf_read_value(client,
						 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;
	}

	mutex_unlock(&data->update_lock);
	return data;
}

/*
 * 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 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),
	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_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),
       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 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, "%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
show_fan_div(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", div_from_reg(data->fan_div[nr]));
}

static ssize_t
store_fan_min(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;
	unsigned int val = simple_strtoul(buf, NULL, 10);
	unsigned int reg;
	u8 new_div;

	mutex_lock(&data->update_lock);
	if (!val) {
		/* No min limit, alarm disabled */
		data->fan_min[nr] = 255;
		new_div = data->fan_div[nr]; /* No change */
		dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
	} else if ((reg = 1350000U / val) >= 128 * 255) {
		/* Speed below this value cannot possibly be represented,
		   even with the highest divider (128) */
		data->fan_min[nr] = 254;
		new_div = 7; /* 128 = (1 << 7) */
		dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
			 "minimum\n", nr + 1, val, fan_from_reg(254, 128));
	} else if (!reg) {
		/* Speed above this value cannot possibly be represented,
		   even with the lowest divider (1) */
		data->fan_min[nr] = 1;
		new_div = 0; /* 1 = (1 << 0) */
		dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
			 "maximum\n", nr + 1, val, fan_from_reg(1, 1));
	} else {
		/* Automatically pick the best divider, i.e. the one such
		   that the min limit will correspond to a register value
		   in the 96..192 range */
		new_div = 0;
		while (reg > 192 && new_div < 7) {
			reg >>= 1;
			new_div++;
		}
		data->fan_min[nr] = reg;
	}

	/* Write both the fan clock divider (if it changed) and the new
	   fan min (unconditionally) */
	if (new_div != data->fan_div[nr]) {
		if (new_div > data->fan_div[nr])
			data->fan[nr] >>= (data->fan_div[nr] - new_div);
		else
			data->fan[nr] <<= (new_div - data->fan_div[nr]);

		dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
			nr + 1, div_from_reg(data->fan_div[nr]),
			div_from_reg(new_div));
		data->fan_div[nr] = new_div;
		w83627ehf_write_fan_div(client, nr);
	}
	w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
			      data->fan_min[nr]);
	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_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),
	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_IRUGO, show_fan_div, NULL, 0),
	SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
	SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
	SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
	SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
};

#define show_temp1_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
	   char *buf) \
{ \
	struct w83627ehf_data *data = w83627ehf_update_device(dev); \
	return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
}
show_temp1_reg(temp1);
show_temp1_reg(temp1_max);
show_temp1_reg(temp1_max_hyst);

#define store_temp1_reg(REG, reg) \
static ssize_t \
store_temp1_##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); \
	u32 val = simple_strtoul(buf, NULL, 10); \
 \
	mutex_lock(&data->update_lock); \
	data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
	w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
			      data->temp1_##reg); \
	mutex_unlock(&data->update_lock); \
	return count; \
}
store_temp1_reg(OVER, max);
store_temp1_reg(HYST, max_hyst);

#define show_temp_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, "%d\n", \
		       LM75_TEMP_FROM_REG(data->reg[nr])); \
}
show_temp_reg(temp);
show_temp_reg(temp_max);
show_temp_reg(temp_max_hyst);

#define store_temp_reg(REG, reg) \
static ssize_t \
store_##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->reg[nr] = LM75_TEMP_TO_REG(val); \
	w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
			      data->reg[nr]); \
	mutex_unlock(&data->update_lock); \
	return count; \
}
store_temp_reg(OVER, temp_max);
store_temp_reg(HYST, temp_max_hyst);

static struct sensor_device_attribute sda_temp[] = {
	SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
	SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
	SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
	SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
		    store_temp1_max, 0),
	SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
		    store_temp_max, 0),
	SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
		    store_temp_max, 1),
	SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
		    store_temp1_max_hyst, 0),
	SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
		    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),
};

#define show_pwm_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, "%d\n", data->reg[nr]); \
}

show_pwm_reg(pwm_mode)
show_pwm_reg(pwm_enable)
show_pwm_reg(pwm)

static ssize_t
store_pwm_mode(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);
	u16 reg;

	if (val > 1)
		return -EINVAL;
	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
	data->pwm_mode[nr] = val;
	reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
	if (!val)
		reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_pwm(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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);

	mutex_lock(&data->update_lock);
	data->pwm[nr] = val;
	w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_pwm_enable(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);
	u16 reg;

	if (!val || (val > 2))	/* only modes 1 and 2 are supported */
		return -EINVAL;
	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
	data->pwm_enable[nr] = val;
	reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
	reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}


#define show_tol_temp(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, "%d\n", temp1_from_reg(data->reg[nr])); \
}

show_tol_temp(tolerance)
show_tol_temp(target_temp)

static ssize_t
store_target_temp(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;
	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);

	mutex_lock(&data->update_lock);
	data->target_temp[nr] = val;
	w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_tolerance(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;
	u16 reg;
	/* Limit the temp to 0C - 15C */
	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);

	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
	data->tolerance[nr] = val;
	if (nr = 1)
		reg = (reg & 0x0f) | (val << 4);
	else
		reg = (reg & 0xf0) | val;
	w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}

static struct sensor_device_attribute sda_pwm[] = {
	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),
	SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
};

static struct sensor_device_attribute sda_pwm_mode[] = {
	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 0),
	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 1),
	SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 2),
	SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 3),
};

static struct sensor_device_attribute sda_pwm_enable[] = {
	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 0),
	SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 1),
	SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 2),
	SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 3),
};

static struct sensor_device_attribute sda_target_temp[] = {
	SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 0),
	SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 1),
	SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 2),
	SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 3),
};

static struct sensor_device_attribute sda_tolerance[] = {
	SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 0),
	SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 1),
	SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 2),
	SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 3),
};

/* Smart Fan registers */

#define fan_functions(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, "%d\n", data->reg[nr]); \
}\
static ssize_t \
store_##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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
	mutex_lock(&data->update_lock); \
	data->reg[nr] = val; \
	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr],  val); \
	mutex_unlock(&data->update_lock); \
	return count; \
}

fan_functions(fan_min_output, FAN_MIN_OUTPUT)

#define fan_time_functions(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, "%d\n", \
			step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
} \
\
static ssize_t \
store_##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; \
	u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
					data->pwm_mode[nr]); \
	mutex_lock(&data->update_lock); \
	data->reg[nr] = val; \
	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
	mutex_unlock(&data->update_lock); \
	return count; \
} \

fan_time_functions(fan_stop_time, FAN_STOP_TIME)


static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
	SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 3),
	SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 3),
};

static struct sensor_device_attribute sda_sf3_arrays[] = {
	SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 0),
	SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 1),
	SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 2),
	SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 0),
	SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 1),
	SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 2),
};

/*
 * Driver and client management
 */

static void w83627ehf_device_remove_files(struct device *dev)
{
	/* some entries in the following arrays may not have been used in
	 * device_create_file(), but device_remove_file() will ignore them */
	int i;

	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
	for (i = 0; i < w83627ehf_num_in; i++) {
		device_remove_file(dev, &sda_in_input[i].dev_attr);
		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
		device_remove_file(dev, &sda_in_min[i].dev_attr);
		device_remove_file(dev, &sda_in_max[i].dev_attr);
	}
	for (i = 0; i < 5; i++) {
		device_remove_file(dev, &sda_fan_input[i].dev_attr);
		device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
		device_remove_file(dev, &sda_fan_div[i].dev_attr);
		device_remove_file(dev, &sda_fan_min[i].dev_attr);
	}
	for (i = 0; i < 4; i++) {
		device_remove_file(dev, &sda_pwm[i].dev_attr);
		device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
		device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
		device_remove_file(dev, &sda_target_temp[i].dev_attr);
		device_remove_file(dev, &sda_tolerance[i].dev_attr);
	}
	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
		device_remove_file(dev, &sda_temp[i].dev_attr);
}

static struct i2c_driver w83627ehf_driver;

static void w83627ehf_init_client(struct i2c_client *client)
{
	int i;
	u8 tmp;

	/* Start monitoring is needed */
	tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
	if (!(tmp & 0x01))
		w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
				      tmp | 0x01);

	/* Enable temp2 and temp3 if needed */
	for (i = 0; i < 2; i++) {
		tmp = w83627ehf_read_value(client,
					   W83627EHF_REG_TEMP_CONFIG[i]);
		if (tmp & 0x01)
			w83627ehf_write_value(client,
					      W83627EHF_REG_TEMP_CONFIG[i],
					      tmp & 0xfe);
	}
}

static int w83627ehf_detect(struct i2c_adapter *adapter)
{
	struct i2c_client *client;
	struct w83627ehf_data *data;
	struct device *dev;
	u8 fan4pin, fan5pin;
	int i, err = 0;

	if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
	                    w83627ehf_driver.driver.name)) {
		err = -EBUSY;
		goto exit;
	}

	if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
		err = -ENOMEM;
		goto exit_release;
	}

	client = &data->client;
	i2c_set_clientdata(client, data);
	client->addr = address;
	mutex_init(&data->lock);
	client->adapter = adapter;
	client->driver = &w83627ehf_driver;
	client->flags = 0;
	dev = &client->dev;

	if (w83627ehf_num_in = 9)
		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);

	data->valid = 0;
	mutex_init(&data->update_lock);

	/* Tell the i2c layer a new client has arrived */
	if ((err = i2c_attach_client(client)))
		goto exit_free;

	/* Initialize the chip */
	w83627ehf_init_client(client);

	/* A few vars need to be filled upon startup */
	for (i = 0; i < 5; i++)
		data->fan_min[i] = w83627ehf_read_value(client,
				   W83627EHF_REG_FAN_MIN[i]);

	/* fan4 and fan5 share some pins with the GPIO and serial flash */

	superio_enter();
	fan5pin = superio_inb(0x24) & 0x2;
	fan4pin = superio_inb(0x29) & 0x6;
	superio_exit();

	/* It looks like fan4 and fan5 pins can be alternatively used
	   as fan on/off switches, but fan5 control is write only :/
	   We assume that if the serial interface is disabled, designers
	   connected fan5 as input unless they are emitting log 1, which
	   is not the default. */

	data->has_fan = 0x07; /* fan1, fan2 and fan3 */
	i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
	if ((i & (1 << 2)) && (!fan4pin))
		data->has_fan |= (1 << 3);
	if (!(i & (1 << 1)) && (!fan5pin))
		data->has_fan |= (1 << 4);

	/* Register sysfs hooks */
  	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
		if ((err = device_create_file(dev,
			&sda_sf3_arrays[i].dev_attr)))
			goto exit_remove;

	/* if fan4 is enabled create the sf3 files for it */
	if (data->has_fan & (1 << 3))
		for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
			if ((err = device_create_file(dev,
				&sda_sf3_arrays_fan4[i].dev_attr)))
				goto exit_remove;
		}

	for (i = 0; i < w83627ehf_num_in; i++)
		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_alarm[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_min[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_max[i].dev_attr)))
			goto exit_remove;

	for (i = 0; i < 5; i++) {
		if (data->has_fan & (1 << i)) {
			if ((err = device_create_file(dev,
					&sda_fan_input[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_alarm[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_div[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_min[i].dev_attr)))
				goto exit_remove;
			if (i < 4 && /* w83627ehf only has 4 pwm */
				((err = device_create_file(dev,
					&sda_pwm[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_pwm_mode[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_pwm_enable[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_target_temp[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_tolerance[i].dev_attr))))
				goto exit_remove;
		}
	}

	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
		if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
			goto exit_remove;

	data->class_dev = hwmon_device_register(dev);
	if (IS_ERR(data->class_dev)) {
		err = PTR_ERR(data->class_dev);
		goto exit_remove;
	}

	return 0;

exit_remove:
	w83627ehf_device_remove_files(dev);
	i2c_detach_client(client);
exit_free:
	kfree(data);
exit_release:
	release_region(address + REGION_OFFSET, REGION_LENGTH);
exit:
	return err;
}

static int w83627ehf_detach_client(struct i2c_client *client)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int err;

	hwmon_device_unregister(data->class_dev);
	w83627ehf_device_remove_files(&client->dev);

	if ((err = i2c_detach_client(client)))
		return err;
	release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
	kfree(data);

	return 0;
}

static struct i2c_driver w83627ehf_driver = {
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "w83627ehf",
	},
	.attach_adapter	= w83627ehf_detect,
	.detach_client	= w83627ehf_detach_client,
};

static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
{
	u16 val;

	REG = sioaddr;
	VAL = sioaddr + 1;
	superio_enter();

	val = (superio_inb(SIO_REG_DEVID) << 8)
	    | superio_inb(SIO_REG_DEVID + 1);
	switch (val & SIO_ID_MASK) {
	case SIO_W83627DHG_ID:
		w83627ehf_num_in = 9;
		break;
	case SIO_W83627EHF_ID:
	case SIO_W83627EHG_ID:
		w83627ehf_num_in = 10;
		break;
	default:
		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
			val);
		superio_exit();
		return -ENODEV;
	}

	superio_select(W83627EHF_LD_HWM);
	val = (superio_inb(SIO_REG_ADDR) << 8)
	    | superio_inb(SIO_REG_ADDR + 1);
	*addr = val & REGION_ALIGNMENT;
	if (*addr = 0) {
		superio_exit();
		return -ENODEV;
	}

	/* Activate logical device if needed */
	val = superio_inb(SIO_REG_ENABLE);
	if (!(val & 0x01))
		superio_outb(SIO_REG_ENABLE, val | 0x01);

	superio_exit();
	return 0;
}

static int __init sensors_w83627ehf_init(void)
{
	if (w83627ehf_find(0x2e, &address)
	 && w83627ehf_find(0x4e, &address))
		return -ENODEV;

	return i2c_isa_add_driver(&w83627ehf_driver);
}

static void __exit sensors_w83627ehf_exit(void)
{
	i2c_isa_del_driver(&w83627ehf_driver);
}

MODULE_AUTHOR("Jean Delvare <khali at linux-fr.org>");
MODULE_DESCRIPTION("W83627EHF driver");
MODULE_LICENSE("GPL");

module_init(sensors_w83627ehf_init);
module_exit(sensors_w83627ehf_exit);
-------------- next part --------------
#!/bin/bash

if [ $UID != 0 ]; then
    echo "This script tests the w83627ehf module. It must be run as root."
    exit 1
fi

echo "* WARNING: This will run your system fans through many possible"
echo "           combinations. There is a possibility your system will"
echo "           overheat. Use this script at your own risk!"

# locate the w83627ehf in the sysfs
SYS="`awk '{if (\$3=\"sysfs\") {print \$2;exit}}' /etc/mtab`"
#'
found_w83627ehf=""
if [ -d $SYS/class/hwmon ]; then
    for a in $SYS/class/hwmon/*; do
        if [ "`cat $a/device/name`" = "w83627ehf" ]; then
            found_w83627ehf="$a"
	fi
    done
fi
if [ -z "$found_w83627ehf" ]; then
    echo "No w83627ehf driver in system. Loading module."
    modprobe w83627ehf || exit 1
else
    if lsmod | grep -q w83627ehf; then
        if modprobe -r w83627ehf && modprobe w83627ehf; then
            :
        else
            echo "Module w83627ehf: modprobe and unload failed."
            exit 1
        fi
    fi
fi

# check sysfs entries, permissions
hw=${found_w83627ehf/$SYS\/class\/hwmon\//}
#`
cd "$found_w83627ehf/device"
ls -l | cut -c-28,42- | (
    d=0
    check_line()
    {
        read a || exit 1
        if [ "$a" != "$1" ]; then
            if [ $d = 0 ]; then
                echo "Permissions or files mismatched ($found_w83627ehf/device)"
            fi
            echo "$a"
            d=1
        fi
    }
    check_line_skip()
    {
        while true; do
            if ! read a; then
                if [ $d = 0 ]; then
                    echo "xPermissions or files mismatched ($found_w83627ehf/device)"
                fi
                d=1
                break
            fi
            if [ "$a" = "$1" ]; then
                break
            fi
        done
    }
    check_line "total 0"
    check_line "lrwxrwxrwx 1 root root    0 bus -> ../../../../bus/i2c"
    l="driver -> ../../../../bus/i2c/drivers/w83627ehf"
    check_line "lrwxrwxrwx 1 root root    0 $l"
    check_line "-r--r--r-- 1 root root 4096 fan1_alarm"
    check_line "-r--r--r-- 1 root root 4096 fan1_div"
    check_line "-r--r--r-- 1 root root 4096 fan1_input"
    check_line "-rw-r--r-- 1 root root 4096 fan1_min"
    #check_line "-rw-r--r-- 1 root root 4096 fan1_min_output"
    #check_line "-rw-r--r-- 1 root root 4096 fan1_stop_time"
    check_line "-r--r--r-- 1 root root 4096 fan2_alarm"
    check_line "-r--r--r-- 1 root root 4096 fan2_div"
    check_line "-r--r--r-- 1 root root 4096 fan2_input"
    #check_line "-rw-r--r-- 1 root root 4096 fan2_max_output"
    check_line "-rw-r--r-- 1 root root 4096 fan2_min"
    #check_line "-rw-r--r-- 1 root root 4096 fan2_min_output"
    #check_line "-rw-r--r-- 1 root root 4096 fan2_step"
    #check_line "-rw-r--r-- 1 root root 4096 fan2_stop_time"
    check_line "-r--r--r-- 1 root root 4096 fan3_alarm"
    check_line "-r--r--r-- 1 root root 4096 fan3_div"
    check_line "-r--r--r-- 1 root root 4096 fan3_input"
    check_line "-rw-r--r-- 1 root root 4096 fan3_min"
    #check_line "-rw-r--r-- 1 root root 4096 fan3_min_output"
    #check_line "-rw-r--r-- 1 root root 4096 fan3_stop_time"
    # skips any fan4_*, fan5_* entries...this could be done better
    # check_line "-r--r--r-- 1 root root 4096 fan4_alarm"
    # check_line "-r--r--r-- 1 root root 4096 fan4_div"
    # check_line "-r--r--r-- 1 root root 4096 fan4_input"
    # check_line "-rw-r--r-- 1 root root 4096 fan4_max_output"
    # check_line "-rw-r--r-- 1 root root 4096 fan4_min"
    # check_line "-rw-r--r-- 1 root root 4096 fan4_min_output"
    # check_line "-rw-r--r-- 1 root root 4096 fan4_step"
    # check_line "-rw-r--r-- 1 root root 4096 fan4_stop_time"
    #check_line "-rw-r--r-- 1 root root 4096 fan_step_down_time"
    #check_line "-rw-r--r-- 1 root root 4096 fan_step_up_time"
    l="hwmon:$hw -> ../../../../class/hwmon/$hw"
    check_line_skip "lrwxrwxrwx 1 root root    0 $l"
    check_line "-r--r--r-- 1 root root 4096 in0_alarm"
    check_line "-r--r--r-- 1 root root 4096 in0_input"
    check_line "-rw-r--r-- 1 root root 4096 in0_max"
    check_line "-rw-r--r-- 1 root root 4096 in0_min"
    check_line "-r--r--r-- 1 root root 4096 in1_alarm"
    check_line "-r--r--r-- 1 root root 4096 in1_input"
    check_line "-rw-r--r-- 1 root root 4096 in1_max"
    check_line "-rw-r--r-- 1 root root 4096 in1_min"
    check_line "-r--r--r-- 1 root root 4096 in2_alarm"
    check_line "-r--r--r-- 1 root root 4096 in2_input"
    check_line "-rw-r--r-- 1 root root 4096 in2_max"
    check_line "-rw-r--r-- 1 root root 4096 in2_min"
    check_line "-r--r--r-- 1 root root 4096 in3_alarm"
    check_line "-r--r--r-- 1 root root 4096 in3_input"
    check_line "-rw-r--r-- 1 root root 4096 in3_max"
    check_line "-rw-r--r-- 1 root root 4096 in3_min"
    check_line "-r--r--r-- 1 root root 4096 in4_alarm"
    check_line "-r--r--r-- 1 root root 4096 in4_input"
    check_line "-rw-r--r-- 1 root root 4096 in4_max"
    check_line "-rw-r--r-- 1 root root 4096 in4_min"
    check_line "-r--r--r-- 1 root root 4096 in5_alarm"
    check_line "-r--r--r-- 1 root root 4096 in5_input"
    check_line "-rw-r--r-- 1 root root 4096 in5_max"
    check_line "-rw-r--r-- 1 root root 4096 in5_min"
    check_line "-r--r--r-- 1 root root 4096 in6_alarm"
    check_line "-r--r--r-- 1 root root 4096 in6_input"
    check_line "-rw-r--r-- 1 root root 4096 in6_max"
    check_line "-rw-r--r-- 1 root root 4096 in6_min"
    check_line "-r--r--r-- 1 root root 4096 in7_alarm"
    check_line "-r--r--r-- 1 root root 4096 in7_input"
    check_line "-rw-r--r-- 1 root root 4096 in7_max"
    check_line "-rw-r--r-- 1 root root 4096 in7_min"
    check_line "-r--r--r-- 1 root root 4096 in8_alarm"
    check_line "-r--r--r-- 1 root root 4096 in8_input"
    check_line "-rw-r--r-- 1 root root 4096 in8_max"
    check_line "-rw-r--r-- 1 root root 4096 in8_min"
    # skips any in9_* entries...this could be done better
    #check_line "-r--r--r-- 1 root root 4096 in9_alarm"
    #check_line "-r--r--r-- 1 root root 4096 in9_input"
    #check_line "-rw-r--r-- 1 root root 4096 in9_max"
    #check_line "-rw-r--r-- 1 root root 4096 in9_min"
    check_line_skip "-r--r--r-- 1 root root 4096 name"
    check_line "drwxr-xr-x 2 root root    0 power"
    check_line "-rw-r--r-- 1 root root 4096 pwm1"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_enable"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_min_output"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_mode"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_stop_time"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_target"
    check_line "-rw-r--r-- 1 root root 4096 pwm1_tolerance"
    check_line "-rw-r--r-- 1 root root 4096 pwm2"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_enable"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_min_output"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_mode"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_stop_time"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_target"
    check_line "-rw-r--r-- 1 root root 4096 pwm2_tolerance"
    check_line "-rw-r--r-- 1 root root 4096 pwm3"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_enable"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_min_output"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_mode"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_stop_time"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_target"
    check_line "-rw-r--r-- 1 root root 4096 pwm3_tolerance"
    # skips any pwm4_* entries...this could be done better
    check_line_skip "-r--r--r-- 1 root root 4096 temp1_alarm"
    check_line "-r--r--r-- 1 root root 4096 temp1_input"
    check_line "-rw-r--r-- 1 root root 4096 temp1_max"
    check_line "-rw-r--r-- 1 root root 4096 temp1_max_hyst"
    #check_line "-rw-r--r-- 1 root root 4096 temp1_target"
    #check_line "-rw-r--r-- 1 root root 4096 temp1_tolerance"
    check_line "-r--r--r-- 1 root root 4096 temp2_alarm"
    check_line "-r--r--r-- 1 root root 4096 temp2_input"
    check_line "-rw-r--r-- 1 root root 4096 temp2_max"
    check_line "-rw-r--r-- 1 root root 4096 temp2_max_hyst"
    #check_line "-rw-r--r-- 1 root root 4096 temp2_target"
    #check_line "-rw-r--r-- 1 root root 4096 temp2_tolerance"
    check_line "-r--r--r-- 1 root root 4096 temp3_alarm"
    check_line "-r--r--r-- 1 root root 4096 temp3_input"
    check_line "-rw-r--r-- 1 root root 4096 temp3_max"
    check_line "-rw-r--r-- 1 root root 4096 temp3_max_hyst"
    #check_line "-rw-r--r-- 1 root root 4096 temp3_target"
    #check_line "-rw-r--r-- 1 root root 4096 temp3_tolerance"
    # skip any temp_4* entires...this could be done better
    #check_line "-rw-r--r-- 1 root root 4096 temp4_target"
    #check_line "-rw-r--r-- 1 root root 4096 temp4_tolerance"
    check_line_skip "--w------- 1 root root 4096 uevent"
    if read a; then echo "$a"; d=1; fi
    [ $d != 0 ] && exit 1
    :
    ) || exit 1

# input voltages
# *** BUG? *** Are these formulas still being used? - dh
#    label in0 "VCore"
#    label in1 "VIN0"
#    compute in1 @*(1+(56/10)),  @/(1+(56/10))
#    label in2 "AVCC"
#    label in3 "3VCC"
#    label in4 "VIN1"
#    label in5 "VIN2"
#    label in6 "VIN3"
#    compute in6 @*(1+(22/10)),  @/(1+(22/10))
#    label in7 "VSB"
#    label in8 "VBAT"
#    label in9 "VIN4"

check_voltage()
{
    V="`cat $1_input`" || exit 1
    L="`cat $1_min`" || exit 1
    H="`cat $1_max`" || exit 1
    A="`cat $1_alarm`" || exit 1
    if [ $V -lt $L -a $A = 0 ]; then
        echo "Sensor   mV   min   max"
        printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
        echo "  Volts below min, alarm didn't go off."
        exit 1
    fi
    if [ $V -gt $H -a $A = 0 ]; then
        echo "Sensor   mV   min   max"
        printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
        echo "  Volts above max, alarm didn't go off."
        exit 1
    fi
    if [ $V -gt $L -a $V -lt $H -a $A != 0 ]; then
        echo "Sensor   mV   min   max"
        printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
        echo "  Volts between max and min but alarm went off."
        exit 1
    fi
}
check_voltage in0 "VCore" ""
check_voltage in1 "+12V" "*66/10"
check_voltage in2 "AVCC" ""
check_voltage in3 "3VCC" ""
check_voltage in4 "VIN1" ""
check_voltage in5 "VIN2" ""
check_voltage in6 "+5V" "*32/10"
check_voltage in7 "VSB" ""
check_voltage in8 "VBAT" ""
check_voltage in9 "VIN4" ""

check_temp()
{
    T="`cat $1_input`" || exit 1
    if [ "$T" -lt "1" ]; then
        echo "$1 too low: $T"
        exit 1
    fi
}
check_temp temp1
check_temp temp2
check_temp temp3

test_smartfan1()
{
# fan$1_min_output is the minimum $2 will go to
# why doesn't the fan stop?
# maybe fan$1_min controls where fan$1_alarm is triggered
#
# SmartFan III items: fan2_step, fan2_max_output, fan4_step, fan4_max_output
    echo ""
    echo "test_smartfan1 \$1=\"$1\" \$2=\"$2\""

    # ********* MUST VERIFY THAT fan_step_time IS CORRECT (PWM->100 ms, DC->400 ms)
    # while $2 < 255 lower temp$1_target +/- temp$1_tolerance,
    #     verify that $2 increases and temp$1_input decreases
    # while $2 > fan$1_min_output increase temp$1_target +/- temp$1_tolerance,
    #     verify that $2 decreases, stop if temp$1_input >= MIN(target+tolerance, 60000)
    # restore temp$1_target
}
#test_smartfan1 3 pwm3

#exit 0

# build a mapping from pwm output to fan rpm input
echo "Mapping fans...setting all PWMs to 100%"
for a in pwm*_enable; do
    p=${a/_enable/}
    echo 1 > $a || exit 1
    echo 255 > $p || exit 1
done

sleep 1

read_fan_rpm()
{
    # take three readings. if they are not within 45% of each other, try again
    retry=2
    while [ $retry != 0 ]; do
        sleep 2
        t1="`cat fan$1_input`" || exit 1
        sleep 2
        t2="`cat fan$1_input`" || exit 1
        sleep 2
        t3="`cat fan$1_input`" || exit 1
        l="$t1"
        [ "$t2" -lt "$l" ] && l="$t2"
        [ "$t3" -lt "$l" ] && l="$t3"
        h="$t1"
        [ "$t2" -gt "$h" ] && h="$t2"
        [ "$t3" -gt "$h" ] && h="$t3"
        if [ $l -gt 0 ]; then
            h=$(($h-$l))
            h=$(($h*10/$l))
            [ "$h" -lt "4" ] && break
        fi
        retry=$(($retry-1))
    done
}

for a in fan*_div; do
    i=${a/fan/}
    i=${i/_div/}
    map[$i]=""
    rpm="`cat fan${i}_input`" || exit 1
    if [ "`cat $a`" = "128" -a "$rpm" = "0" ]; then
        echo "    fan $i $rpm RPM: ignored. (Fan not installed?)"
        continue
    fi

    # take the max of two readings
    sleep 2
    t="`cat fan${i}_input`" || exit 1
    [ "$t" -gt "$rpm" ] && rpm="$t"

    echo "    fan $i $rpm RPM:"
    m=""
    n=""
    for b in pwm*_enable; do
        echo 1 > ${b/_enable/}_mode || exit 1
        echo 27 > ${b/_enable/} || exit 1
        read_fan_rpm $i
        echo 255 > ${b/_enable/} || exit 1
        if [ $retry = 0 ]; then
            echo "        ${b/_enable/} RPM highly variable? ignored."
        else
            d=$(($rpm-$l))
            d=$(($d*4/$rpm))
            if [ "$d" -gt "1" ]; then
                echo "        ${b/_enable/} $l RPM: connected."
                if [ -z "$m" ]; then
                    m="${b/_enable/}"
                else
                    n="${b/_enable/}"
                fi
                sleep 1
            else
                echo "        ${b/_enable/} $l RPM"
            fi
        fi
        if [ -n "$n" ]; then
            echo "        Multiple PWMs mapped to fan? ignored."
        else
            map[$i]="$m"
        fi
    done
done

set_pwm_dc()
{
    echo $2 > $1_mode || exit 1
    u="`cat $1_mode`" || exit 1
    if [ "$u" != "$2" ]; then
        [ "$2" = "0" ] && echo -e "\nFailure: could not set PWM mode."
        [ "$2" != "0" ] && echo -e "\nFailure: could not set DC mode."
        exit 1
    fi
}

for a in fan*_div; do
    i=${a/fan/}
    i=${i/_div/}
    [ -z "${map[$i]}" ] && continue
    echo -n "Testing fan $i - ${map[$i]} ..."
    # 1. test clock divider auto-scaling (high RPM and low RPM)
    mún${i}_min
    echo 1 > $m || exit 1
    u="`cat $m`" || exit 1
    v="`cat $a`" || exit 1
    if [ "$u" -lt "41" -o "$v" != "128" ]; then
        echo -e "\nFailure: $m = `cat $m` (not 41)  $a = `cat $a` (not 128)"
        exit 1
    fi
    dd
    for b in 210 421 843 1687 3375 6750 13500; do
        echo $b > $m || exit 1
        u="`cat $m`" || exit 1
        v="`cat $a`" || exit 1
        if [ "$u" != "$b" -o "$v" != "$d" ]; then
            # one retry, cause we are in a race condition with the driver
            echo $b > $m || exit 1
            u="`cat $m`" || exit 1
            v="`cat $a`" || exit 1
            if [ "$u" != "$b" -o "$v" != "$d" ]; then
                echo -e "\nFailure: $m = $u (not $b)  $a = $v (not $d)"
                exit 1
            fi
        fi
        d=$(($d/2))
    done
    echo -n " div OK"
    # 2. test PWM vs DC mode
    u="`cat ${map[$i]}_mode`" || exit 1
    if [ "$u" != "1" ]; then
        echo -e "\nLogic error: not in PWM mode"
        exit 1
    fi
    read_fan_rpm $i
    pwm_rpm=$l
    set_pwm_dc ${map[$i]} 0 # DC mode
    read_fan_rpm $i
    d=$(($l-$pwm_rpm))
    [ $d -lt 0 ] && d=$((-$d))
    d=$(($d*10/$l))
    if [ $d -gt 4 ]; then
        echo -e "\nFailure: PWM $pwm_rpm RPM, DC $l RPM at 100%."
        exit 1
    fi
    set_pwm_dc ${map[$i]} 1 # PWM mode
    echo 128 > ${map[$i]} || exit 1
    read_fan_rpm $i
    pwm_rpm=$l
    set_pwm_dc ${map[$i]} 0 # DC mode
    read_fan_rpm $i
    d=$(($l-$pwm_rpm))
    [ $d -lt 0 ] && d=$((-$d))
    d=$(($d*10/$l))
    if [ $d -lt 4 ]; then
        echo -e "\nFailure: PWM $pwm_rpm RPM, DC $l RPM at 50%."
        exit 1
    fi
    echo -n ", PWM/DC OK"
    if false; then
        # disabled mode is not in the driver anymore...
        # test disabled mode (RPM should increase)
        echo 0 > ${map[$i]}_enable || exit 1
        lorpm=$l
        read_fan_rpm $i
        d=$(($l-$lorpm))
        d=$(($d*10/$lorpm))
        if [ $d -lt 4 ]; then
            echo -e "\nFailure: 50% $lorpm RPM, disabled (100%) $l RPM."
            exit 1
        fi
        echo 1 > ${map[$i]}_enable || exit 1
        echo -n ", enable OK"
    fi

    # 4. test SmartFan I mode (thermal cruise)
    echo 2 > ${map[$i]}_enable || exit 1
    #test_smartfan1 "$i" "${map[$i]}"
    echo ", SF1 OK"
done

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (3 preceding siblings ...)
  2006-12-26  8:02 ` David Hubbard
@ 2006-12-26 10:31 ` Hamlet
  2006-12-26 23:14 ` David Hubbard
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Hamlet @ 2006-12-26 10:31 UTC (permalink / raw)
  To: lm-sensors

Thank you for your effort David.
I am using gentoo-sources-2.6.18-r5
The entire patch list is available in:
http://dev.gentoo.org/~dsd/genpatches/patches-2.6.18-7.htm

So I copied the original w83627ehf.c to w83627ehf.c.orig
And used your w83627ehf.c.txt as w83627ehf.c

The modules works but I am not sure if the numbers are all right.
The attatchment is the screenshot of gkrellm2
-------------- next part --------------
A non-text attachment was scrubbed...
Name: w83627dhg.jpg
Type: image/jpeg
Size: 68374 bytes
Desc: not available
Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20061226/3b1578a7/attachment-0001.jpg 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (4 preceding siblings ...)
  2006-12-26 10:31 ` Hamlet
@ 2006-12-26 23:14 ` David Hubbard
  2006-12-27  1:10 ` Hamlet
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-26 23:14 UTC (permalink / raw)
  To: lm-sensors

Hi Hamlet,

It looks like your temp3 is wrong and your voltages need a little
tweaking. To fix the voltages, you will need to edit your
sensors.conf. That part is not too hard.

temp1 is 'SYSTIN,' temp2 is 'CPUTIN,' and temp3 is 'AUXTIN.'  I think
your system temp and CPU temp look right. Does your AUXTIN value
change at all? It may be floating or pulled high -- both are
motherboard design decisions which mean that the temperature input is
useless. You can add a line to sensors.conf to ignore the temperature.

Hope that helps,
David


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (5 preceding siblings ...)
  2006-12-26 23:14 ` David Hubbard
@ 2006-12-27  1:10 ` Hamlet
  2006-12-28 22:53 ` Hamlet
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Hamlet @ 2006-12-27  1:10 UTC (permalink / raw)
  To: lm-sensors

Hi David.
Thank you again for your help.
But I couldn't resolve the problem.

temp3 changes between 124-127
+12V, -12V, -5V seem to be wrong and maybe others too.

Maybe I have to edit .gkrellm2/sensor-config but don't know how

Another problem I have is "sensors" from lm-sensors.
I already added these lines to /etc/sensors.conf:

# Winbond W83627DHG configuration
# This is for an Asus P5B Deluxe WiFi-AP Edition
chip "w83627dhg-*"
# Temperatures
label temp1 "SYS Temp"
label temp2 "CPU Temp"
label temp3 "AUX Temp"
# Fans
label fan1 "Fan1"
label fan2 "CPU Fan"
label fan3 "Fan3"
label fan4 "Fan4"
label fan5 "Fan5"
# Voltages
label in0 "+3.3V"
label in1 "Vtt1.2V"
label in2 "Vram"
label in3 "Vchip"
label in4 "+5V"
label in5 "+12V"
label in6 "Vcc1.5V"
label in7 "Vcore"
label in8 "5VSB"

But "sensors" command doesn't show any value.
It just says the chipset:

w83627dhg-isa-0290
Adapter: ISA adapter

Do I have to update lm-sensors package?
I am using 2.10.0 version


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (6 preceding siblings ...)
  2006-12-27  1:10 ` Hamlet
@ 2006-12-28 22:53 ` Hamlet
  2006-12-29  2:35 ` David Hubbard
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Hamlet @ 2006-12-28 22:53 UTC (permalink / raw)
  To: lm-sensors

Hi David.

I have Asus P5B Deluxe WiFi AP Edition with Gentoo.

Below is the content of my .gkrellm2/sensor-config

sensor sensor_config_version 1
sensor sensor_float_factor 10000
sensor gkrellm_float_factor 1000
sensor "MB" "w83627dhg-9191-0290/temp1" 10000 0 1 0
sensor "CPU" "w83627dhg-9191-0290/temp2" 10000 0 1 0
sensor "AUX" "w83627dhg-9191-0290/temp3" 10000 0 0 0
sensor "FAN1" "w83627dhg-9191-0290/fan1" 10000 0 1 0
sensor "CPU" "w83627dhg-9191-0290/fan2" 10000 0 1 0
sensor "NONE" "w83627dhg-9191-0290/fan3" 10000 0 1 0
sensor "FAN3" "w83627dhg-9191-0290/fan4" 10000 0 1 0
sensor "FAN2" "w83627dhg-9191-0290/fan5" 10000 0 1 0
sensor "Vcor1" "w83627dhg-9191-0290/in0" 10000 0 0 0
sensor "Vcor2" "w83627dhg-9191-0290/in1" 10000 0 0 0
sensor "+3.3V" "w83627dhg-9191-0290/in2" 09900 0 0 0
sensor "+5V" "w83627dhg-9191-0290/in3" 15640 0 0 0
sensor "+12V" "w83627dhg-9191-0290/in4" 71000 0 0 0
sensor "-12V" "w83627dhg-9191-0290/in5" -74500 0 0 0
sensor "-5V" "w83627dhg-9191-0290/in6" -30950 0 0 0
sensor "V5SB" "w83627dhg-9191-0290/in7" 10000 0 0 0
sensor "VBat" "w83627dhg-9191-0290/in8" 10000 0 0 0
sensor units_fahrenheit 0
sensor volt_display_mode 0

Left side of these pictures shows Asus Probe II.
I don't have Windows, so just googled for these.
http://www.bugtrack.almico.com/file_download.php?file_id\x197&type=bug
http://www.bugtrack.almico.com/file_download.php?file_id 0&type=bug

# TEMPERATURES

MB 46.0C
CPU 59.0C

As you said I had to disable AUX TEMP.
It shows high temperature and it doesn't make any sense.
And based on the above picture, I renamed SYS to MB

# FANS

The motherboard has 6 fans (one for CPU and 5 for CHASSIS)
fan2 detected CPU_FAN
fan1 detected CHA_FAN1
fan5 detected CHA_FAN2
fan4 detected CHA_FAN3
CHA_FAN4 or CHA_FAN5 seem not to have sensors
Asus Probe II only shows three CHASSIS fans
No idea what POWER means, but seems to be fan3
Maybe it's because I am using ATX not BTX power supply unit

# VOLTAGES

I changed some numbers of 4th column, based on the above pictures.
Now I get these values:
Vcor1 1.22
Vcor2 1.84 Sometimes it changes to 1.83
+3.3 3.26
+5V 5.15 as Asus Probe II shows, sometimes it changes to 5.12
+12V 12.0
-12V -12.0
-5V -5.15 sometimes changes to -5.12
V5SB 3.30
VBat 2.82

Asus Probe II shows only one Vcore and the value is 1.26.
Do I have to choose Vcor2 and adjust the value?
Above picture doesn't mention -12V, -5V, V5SB, VBat.
No clue what V5SB and VBat are, but maybe P5B users could ignore those.


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (7 preceding siblings ...)
  2006-12-28 22:53 ` Hamlet
@ 2006-12-29  2:35 ` David Hubbard
  2006-12-29 14:37 ` Hamlet
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-29  2:35 UTC (permalink / raw)
  To: lm-sensors

Hi Hamlet,

> As you said I had to disable AUX TEMP.
> It shows high temperature and it doesn't make any sense.
> And based on the above picture, I renamed SYS to MB

This is good information, and we should try to put these changes into
the repository. You'll need to work with Jean and Rudolf to get that
done.

Have you compared the voltages, temperatures, and fan speeds sensors
reports with the values you see in your BIOS setup?

David


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (8 preceding siblings ...)
  2006-12-29  2:35 ` David Hubbard
@ 2006-12-29 14:37 ` Hamlet
  2006-12-30  2:54 ` David Hubbard
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Hamlet @ 2006-12-29 14:37 UTC (permalink / raw)
  To: lm-sensors

Hi David.
How can I contact with Jean and Rudolf?

As your advice, I checked the BIOS.
CPU		56?C
MB		38?C

CPU		1600 RPM
CHASSIS FAN 1	N/A
CHASSIS FAN 2	N/A
CHASSIS FAN 3	N/A
POWER FAN	N/A

VCORE		1.208
3.3V		3.280
5V		5.171
12V		12.091

So I adjusted again my .gkrellm2/sensor-config

sensor sensor_config_version 1
sensor sensor_float_factor 10000
sensor gkrellm_float_factor 1000
sensor "MB" "w83627dhg-9191-0290/temp1" 08500 0 1 0
sensor "CPU" "w83627dhg-9191-0290/temp2" 10000 0 1 0
sensor "AUX" "w83627dhg-9191-0290/temp3" 10000 0 0 0
sensor "FAN1" "w83627dhg-9191-0290/fan1" 10000 0 1 0
sensor "CPU" "w83627dhg-9191-0290/fan2" 10000 0 1 0
sensor "PWR" "w83627dhg-9191-0290/fan3" 10000 0 1 0
sensor "FAN3" "w83627dhg-9191-0290/fan4" 10000 0 1 0
sensor "FAN2" "w83627dhg-9191-0290/fan5" 10000 0 1 0
sensor "NONE" "w83627dhg-9191-0290/in0" 10000 0 0 0
sensor "Vcore" "w83627dhg-9191-0290/in1" 06600 0 1 0
sensor "+3.3V" "w83627dhg-9191-0290/in2" 09960 0 1 0
sensor "+5V" "w83627dhg-9191-0290/in3" 15680 0 1 0
sensor "+12V" "w83627dhg-9191-0290/in4" 71500 0 1 0
sensor "-12V" "w83627dhg-9191-0290/in5" -74500 0 0 0
sensor "-5V" "w83627dhg-9191-0290/in6" -30950 0 0 0
sensor "V5SB" "w83627dhg-9191-0290/in7" 10000 0 0 0
sensor "VBat" "w83627dhg-9191-0290/in8" 10000 0 0 0
sensor units_fahrenheit 0
sensor volt_display_mode 0

ASUS P5B Deluxe WiFi AP Edition seems to use:

temp1, temp2
fan1, fan2, fan3, fan4, fan5
in1 (or in0 for Vcore, how can I tell?), in2, in3, in4

At least, these values are shown in Asus Probe II and BIOS.

The attachment is the screenshot of gkrellm2.
It shows only those values enabled and adjusted.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gkrellm.jpg
Type: image/jpeg
Size: 6150 bytes
Desc: not available
Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20061229/244790fb/attachment.jpg 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (9 preceding siblings ...)
  2006-12-29 14:37 ` Hamlet
@ 2006-12-30  2:54 ` David Hubbard
  2007-01-02 18:46 ` Rudolf Marek
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2006-12-30  2:54 UTC (permalink / raw)
  To: lm-sensors

Hi Hamlet,

On 12/29/06, Hamlet <hamletmun at fibertel.com.ar> wrote:
> Hi David.
> How can I contact with Jean and Rudolf?

Be sure to include them in the To: header, as I am doing. Then, *be*
*patient*. They're very busy. However, their information is quite
useful, so it's worth waiting. And they don't miss the messages, even
if it takes a while.

> As your advice, I checked the BIOS.
> CPU             56?C
> MB              38?C
>
> CPU             1600 RPM
> CHASSIS FAN 1   N/A
> CHASSIS FAN 2   N/A
> CHASSIS FAN 3   N/A
> POWER FAN       N/A
>
> VCORE           1.208
> 3.3V            3.280
> 5V              5.171
> 12V             12.091
>
> So I adjusted again my .gkrellm2/sensor-config
>
> sensor sensor_config_version 1
> sensor sensor_float_factor 10000
> sensor gkrellm_float_factor 1000
> sensor "MB" "w83627dhg-9191-0290/temp1" 08500 0 1 0
> sensor "CPU" "w83627dhg-9191-0290/temp2" 10000 0 1 0
> sensor "AUX" "w83627dhg-9191-0290/temp3" 10000 0 0 0
> sensor "FAN1" "w83627dhg-9191-0290/fan1" 10000 0 1 0
> sensor "CPU" "w83627dhg-9191-0290/fan2" 10000 0 1 0
> sensor "PWR" "w83627dhg-9191-0290/fan3" 10000 0 1 0
> sensor "FAN3" "w83627dhg-9191-0290/fan4" 10000 0 1 0
> sensor "FAN2" "w83627dhg-9191-0290/fan5" 10000 0 1 0
> sensor "NONE" "w83627dhg-9191-0290/in0" 10000 0 0 0
> sensor "Vcore" "w83627dhg-9191-0290/in1" 06600 0 1 0
> sensor "+3.3V" "w83627dhg-9191-0290/in2" 09960 0 1 0
> sensor "+5V" "w83627dhg-9191-0290/in3" 15680 0 1 0
> sensor "+12V" "w83627dhg-9191-0290/in4" 71500 0 1 0
> sensor "-12V" "w83627dhg-9191-0290/in5" -74500 0 0 0
> sensor "-5V" "w83627dhg-9191-0290/in6" -30950 0 0 0
> sensor "V5SB" "w83627dhg-9191-0290/in7" 10000 0 0 0
> sensor "VBat" "w83627dhg-9191-0290/in8" 10000 0 0 0
> sensor units_fahrenheit 0
> sensor volt_display_mode 0
>
> ASUS P5B Deluxe WiFi AP Edition seems to use:
>
> temp1, temp2
> fan1, fan2, fan3, fan4, fan5
> in1 (or in0 for Vcore, how can I tell?), in2, in3, in4
>
> At least, these values are shown in Asus Probe II and BIOS.
>
> The attachment is the screenshot of gkrellm2.
> It shows only those values enabled and adjusted.

This looks like the right configuration. I will refer other users
setting up the Asus P5B Deluxe WiFi AP to your message in the future.

David


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (10 preceding siblings ...)
  2006-12-30  2:54 ` David Hubbard
@ 2007-01-02 18:46 ` Rudolf Marek
  2007-01-02 18:46 ` David Holl
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Rudolf Marek @ 2007-01-02 18:46 UTC (permalink / raw)
  To: lm-sensors

Hello,

Sorry for the delay, it is quite hectic days and I still need to read 71 emails
in lm-sensors box...

Maybe I can get conversion formulas for you if you give me the dsdt.bin file.

cat /proc/acpi/dsdt > /tmp/dsdt.bin

This file contains ACPI bytecode and ASUS have there computation formulas/access
to the monitoring chip - the code is called from Asus Probe. I'm not sure if the
gkrellm has support for libsensors or not - I checked version 2.2.10 - Thu Oct
26, 2006 has it. So, for the debug purposes I would like to use the text output
of sensors...

Oh. I have root at one P5B mb :)

Here we go:

apt-get install iasl

axon:~# cat /proc/acpi/dsdt > dsdt.bin
axon:~# iasl -d dsdt.bin

Intel ACPI Component Architecture
AML Disassembler version 20060912 [Dec 20 2006]
Copyright (C) 2000 - 2006 Intel Corporation
Supports ACPI Specification Revision 3.0a

Loading Acpi table from file dsdt.bin
Acpi table [DSDT] successfully installed and loaded
Pass 1 parse of [DSDT]
Pass 2 parse of [DSDT]
Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions)
..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Parsing completed
Disassembly completed, written to "dsdt.dsl"


Now find the ATK0110 device:

Inside there is a register map of the chip:

                    Offset (0x20),

            VCOR,   8,

            V12V,   8,

                    Offset (0x23),

            V33V,   8,

                    Offset (0x25),

            V50V,   8,

                    Offset (0x27),
...

So mapping to lm-sensors is:

in0 VCore
in1 12V
in2 not monitored
in3 3.3V
in4 not monitored
in5 5V


No formulas are used. Just raw value * 8mV so no need for compute lines in
sensors.conf

TEMP1 should be MBTEMP, which is direct value, temp2 is CPU temp, temp3 is not
used in their implementation - but conversion formula is same as for the temp2.
So it is most likely floating or not connected.

As for fans:

fan1 is cpu
fan2 is chassis1
fan3 is chassis2
fan4 is power fan
fan5 is not monitored

All in all it seems that gkrellm is wrong. Please use the version indicated
above, and provide the output of sensors command so we can check that the
readings match the reality (and we trust our tools ;)

David,
Thanks for the support of DHG chip, for future reference check/fix this page ;)
http://www.lm-sensors.org/wiki/AsusFormulaHacking


Rudolf


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (11 preceding siblings ...)
  2007-01-02 18:46 ` Rudolf Marek
@ 2007-01-02 18:46 ` David Holl
  2007-01-19  6:02 ` Daniel Ceregatti
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Holl @ 2007-01-02 18:46 UTC (permalink / raw)
  To: lm-sensors

On 12/26/06, Hamlet <hamletmun at fibertel.com.ar> wrote:
>
> ...
> But "sensors" command doesn't show any value.
> It just says the chipset:
>
> w83627dhg-isa-0290
> Adapter: ISA adapter
>
> Do I have to update lm-sensors package?
> I am using 2.10.0 version
>

Hey Hamlet, I noticed the same thing too about "sensors" not reporting
w83627dhg values, so I submitted a patch which should be included in the
next lm-sensors release:
http://www.lm-sensors.org/ticket/2157

If you can, try compiling the lm-sensors code from the subversion
repository:
http://www.lm-sensors.org/wiki/Download

I think the largest thing I missed in my patch was an example for
sensors.conf for our "Asus P5B Deluxe WiFi AP" motherboard, largely because
I haven't had time to work out the voltage scalings.  (I was more concerned
for the fan speeds.)

In Gentoo, the newer gkrellm version (2.2.10) seems to properly support
libsensors & sensors.conf with the lm_sensors USE-flag.  (I unmasked the
latest gkrellm with echo "=app-admin/gkrellm-2.2.10 ~x86" >>
/etc/portage/package.keywords)

- David Holl
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070102/3cd89a9e/attachment.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (12 preceding siblings ...)
  2007-01-02 18:46 ` David Holl
@ 2007-01-19  6:02 ` Daniel Ceregatti
  2007-01-19  6:31 ` David Hubbard
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Ceregatti @ 2007-01-19  6:02 UTC (permalink / raw)
  To: lm-sensors

Hi,

I have the EVGA 122-CK-NF68-AR motherboard, which supposedly has this
chip. I've patched my 2.6.19.2 vanilla kernel with both the patch in
this email (1 hunk failed in Documentation, no big), and the full file,
to no avail. The module loads, but sensors reports this for both:

w83627dhg-isa-0290
Adapter: ISA adapter

A while back I had spoken with Khali on IRC. He told me to change the
w83627ehf.c (from vanilla 2.6.18 or so at the time) manually. I changed
it thusly (This is against vanilla 2.6.19.2):

-- snip --

--- drivers/hwmon/w83627ehf.c.orig      2007-01-18 21:44:12.065293174 -0800
+++ drivers/hwmon/w83627ehf.c.manually_patched  2007-01-18
21:49:52.365378263 -0800
@@ -65,8 +65,11 @@
 #define SIO_REG_ENABLE         0x30    /* Logical device enable */
 #define SIO_REG_ADDR           0x60    /* Logical device address (2
bytes) */
 
-#define SIO_W83627EHF_ID       0x8840
-#define SIO_ID_MASK            0xFFC0
+//#define SIO_W83627EHF_ID     0x8840
+//#define SIO_ID_MASK          0xFFC0
+
+#define SIO_W83627EHF_ID        0xA020
+#define SIO_ID_MASK            0xFFF0
 
 static inline void
 superio_outb(int reg, int val)

-- snip --

I load this module and run sensors:

w83627ehf-isa-0290
Adapter: ISA adapter
VCore:     +1.41 V  (min =  +0.00 V, max =  +1.74 V)
in1:      +11.14 V  (min =  +8.34 V, max = +11.83 V)
AVCC:      +3.20 V  (min =  +0.10 V, max =  +2.43 V) ALARM
3VCC:      +3.20 V  (min =  +1.15 V, max =  +3.57 V)
in4:       +1.48 V  (min =  +0.15 V, max =  +0.59 V) ALARM
in5:       +1.54 V  (min =  +0.72 V, max =  +1.71 V)
in6:       +5.86 V  (min =  +5.27 V, max =  +0.10 V) ALARM
VSB:       +3.20 V  (min =  +0.18 V, max =  +1.39 V) ALARM
VBAT:      +3.14 V  (min =  +2.64 V, max =  +3.84 V)
in9:       +0.10 V  (min =  +1.74 V, max =  +0.08 V) ALARM
Case Fan:    0 RPM  (min = 4560 RPM, div = 8) ALARM
CPU Fan:  2909 RPM  (min = 2636 RPM, div = 8)
Aux Fan:  1424 RPM  (min = 3879 RPM, div = 4) ALARM
fan4:        0 RPM  (min =  703 RPM, div = 32) ALARM
fan5:        0 RPM  (min = 3375 RPM, div = 8) ALARM
Sys Temp:    -65?C  (high =    +0?C, hyst =  +127?C) 
CPU Temp:  +70.0?C  (high = +127.0?C, hyst =  +0.0?C)   ALARM
AUX Temp:  +57.0?C  (high = +127.0?C, hyst =  +0.0?C) 

I'm guessing (hoping, actually) that most of that is wrong, but at the
very least, I'm getting output.

Any thoughts as to how I should proceed? I suppose I could try the -mm
tree, but I'm hesitant, as vanilla has always served me well.

Daniel Ceregatti

(Please CC me as I'm not on the list. Thanks)

David Hubbard wrote:
> Hello everyone. This email is to let you know that W83627DHG support
> has been added to the kernel source tree (Andrew Morton's -mm branch,
> which is for new drivers).
>
> Jean Delvare wrote:
>> So, your patch is applied, and will be in the next -mm kernel, and
>> will then go in kernel 2.6.21:
>> http://khali.linux-fr.org/devel/linux-2.6/jdelvare-hwmon/hwmon-w83627ehf-add-w83627dhg-support.patch
>>
>
> You are included in this email because some time in the last four
> months you expressed interest in W83627DHG support. If you would like
> to help test the new driver, the instructions are below. Please
> remember to include lm-sensors at lm-sensors.org in any reply messages.
>
> The patch attached to this email and at the URL above applies cleanly
> to linux kernel versions 2.6.19-rc6, 2.6.20-rc1 or 2.6.20-rc2. Kernel
> tarballs are available at:
> http://www.kernel.org/pub/linux/kernel/v2.6/testing/
>
> In addition, the driver may also work for you on 2.6.17 and 2.6.18
> kernels by downloading w83627ehf.c.txt, the second attachment to this
> email. Then save your original driver and copy it into the kernel
> source tree. For example:
>
> # cd /usr/src/linux/drivers/hwmon
> hwmon # cp w83627ehf.c w83627ehf.c.orig
> hwmon # cp w83627ehf.c.txt w83627ehf.c
> hwmon # cd ../..
> linux # make && make modules_install
>
> However, if you find any bugs, the first thing you will have to do is
> upgrade your kernel to the latest version. This is not an official
> backport. There is an official request open for a w83627ehf.c backport
> to linux 2.4. (And I would not mind anyone who wants to jump in on
> that as well.)
>
> The third attachment is a shell script I use to do basic regression
> testing of the driver. If it reports any errors on your system, please
> report them! This driver should not break any functionality from
> previous drivers. The more important test is continuous usage of the
> driver, so please load sensors, ksensors, gkrellm, or whatever
> monitoring tools you normally use, and hit reply if you see anything
> unusual over the next few weeks.
>
> You may need to download the latest version of the sensors source from
> http://www.lm-sensors.org/wiki/Download#SubversionRepository in order
> to have W83627DHG support in user space.
>
> Thanks for your assistance,
> David
>
> Rudolf: Can you update ticket #2142? This should resolve it.
> ------------------------------------------------------------------------
>
> This patch adds support for the w83627dhg chip.
> Signed-off-by: David Hubbard <david.c.hubbard at gmail.com>
>
> --- old/drivers/hwmon/w83627ehf.c	2006-12-21 14:04:19.000000000 -0800
> +++ new/drivers/hwmon/w83627ehf.c	2006-12-24 21:53:54.000000000 -0800
> @@ -32,8 +32,10 @@
>  
>      Supports the following chips:
>  
> -    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
> -    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
> +    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
> +    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
> +                                               0x8860 0xa1
> +    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
>  */
>  
>  #include <linux/module.h>
> @@ -55,8 +57,18 @@
>   * Super-I/O constants and functions
>   */
>  
> +/*
> + * The three following globals are initialized in w83627ehf_find(), before
> + * the i2c-isa device is created. Otherwise, they could be stored in
> + * w83627ehf_data. This is ugly, but necessary. and when the driver is next
> + * updated to become a platform driver, the globals will disappear.
> + */
>  static int REG;		/* The register to read/write */
>  static int VAL;		/* The value to read/write */
> +/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
> + * value is also used in w83627ehf_detect() to export a device name in sysfs
> + * (e.g. w83627ehf or w83627dhg) */
> +static int w83627ehf_num_in;
>  
>  #define W83627EHF_LD_HWM	0x0b
>  
> @@ -65,8 +77,10 @@
>  #define SIO_REG_ENABLE		0x30	/* Logical device enable */
>  #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
>  
> -#define SIO_W83627EHF_ID	0x8840
> -#define SIO_ID_MASK		0xFFC0
> +#define SIO_W83627EHF_ID	0x8850
> +#define SIO_W83627EHG_ID	0x8860
> +#define SIO_W83627DHG_ID	0xa020
> +#define SIO_ID_MASK		0xFFF0
>  
>  static inline void
>  superio_outb(int reg, int val)
> @@ -115,8 +129,12 @@
>  
>  #define W83627EHF_REG_BANK		0x4E
>  #define W83627EHF_REG_CONFIG		0x40
> -#define W83627EHF_REG_CHIP_ID		0x49
> -#define W83627EHF_REG_MAN_ID		0x4F
> +
> +/* Not currently used:
> + * REG_MAN_ID has the value 0x5ca3 for all supported chips.
> + * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
> + * REG_MAN_ID is at port 0x4f
> + * REG_CHIP_ID is at port 0x58 */
>  
>  static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
>  static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
> @@ -429,7 +447,7 @@
>  		}
>  
>  		/* Measured voltages and limits */
> -		for (i = 0; i < 10; i++) {
> +		for (i = 0; i < w83627ehf_num_in; i++) {
>  			data->in[i] = w83627ehf_read_value(client,
>  				      W83627EHF_REG_IN(i));
>  			data->in_min[i] = w83627ehf_read_value(client,
> @@ -1121,7 +1139,7 @@
>  		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
>  	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
>  		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
> -	for (i = 0; i < 10; i++) {
> +	for (i = 0; i < w83627ehf_num_in; i++) {
>  		device_remove_file(dev, &sda_in_input[i].dev_attr);
>  		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
>  		device_remove_file(dev, &sda_in_min[i].dev_attr);
> @@ -1196,7 +1214,11 @@
>  	client->flags = 0;
>  	dev = &client->dev;
>  
> -	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
> +	if (w83627ehf_num_in = 9)
> +		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
> +	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
> +		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
> +
>  	data->valid = 0;
>  	mutex_init(&data->update_lock);
>  
> @@ -1246,7 +1268,7 @@
>  				goto exit_remove;
>  		}
>  
> -	for (i = 0; i < 10; i++)
> +	for (i = 0; i < w83627ehf_num_in; i++)
>  		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
>  			|| (err = device_create_file(dev,
>  				&sda_in_alarm[i].dev_attr))
> @@ -1340,7 +1362,17 @@
>  
>  	val = (superio_inb(SIO_REG_DEVID) << 8)
>  	    | superio_inb(SIO_REG_DEVID + 1);
> -	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
> +	switch (val & SIO_ID_MASK) {
> +	case SIO_W83627DHG_ID:
> +		w83627ehf_num_in = 9;
> +		break;
> +	case SIO_W83627EHF_ID:
> +	case SIO_W83627EHG_ID:
> +		w83627ehf_num_in = 10;
> +		break;
> +	default:
> +		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
> +			val);
>  		superio_exit();
>  		return -ENODEV;
>  	}
> --- old/Documentation/hwmon/w83627ehf	2006-12-21 14:04:19.000000000 -0800
> +++ new/Documentation/hwmon/w83627ehf	2006-12-24 21:55:46.000000000 -0800
> @@ -2,26 +2,29 @@
>  ===========>  
>  Supported chips:
> -  * Winbond W83627EHF/EHG (ISA access ONLY)
> +  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
>      Prefix: 'w83627ehf'
>      Addresses scanned: ISA address retrieved from Super I/O registers
> -    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
> +    Datasheet:
> +        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
> +        DHG datasheet confidential.
>  
>  Authors:
>          Jean Delvare <khali at linux-fr.org>
>          Yuan Mu (Winbond)
>          Rudolf Marek <r.marek at assembler.cz>
> +        David Hubbard <david.c.hubbard at gmail.com>
>  
>  Description
>  -----------
>  
> -This driver implements support for the Winbond W83627EHF and W83627EHG
> -super I/O chips. We will refer to them collectively as Winbond chips.
> +This driver implements support for the Winbond W83627EHF, W83627EHG, and
> +W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
>  
>  The chips implement three temperature sensors, five fan rotation
> -speed sensors, ten analog voltage sensors, alarms with beep warnings (control
> -unimplemented), and some automatic fan regulation strategies (plus manual
> -fan control mode).
> +speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
> +with beep warnings (control unimplemented), and some automatic fan regulation
> +strategies (plus manual fan control mode).
>  
>  Temperatures are measured in degrees Celsius and measurement resolution is 1
>  degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
> @@ -55,6 +58,9 @@
>  /sys files
>  ----------
>  
> +name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
> +       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg"
> +
>  pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
>  	   0 (stop) to 255 (full)
>  
> @@ -83,3 +89,36 @@
>  
>  Note: last two functions are influenced by other control bits, not yet exported
>        by the driver, so a change might not have any effect.
> +
> +Implementation Details
> +----------------------
> +Future driver development should bear in mind that the following registers have
> +different functions on the 627EHF and the 627DHG. Some registers also have
> +different power-on default values, but BIOS should already be loading
> +appropriate defaults. Note that bank selection must be performed as is currently
> +done in the driver for all register addresses.
> +
> +0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
> +0x4a:  not completely documented for the EHF and the DHG documentation assigns
> +       different behavior to bits 7 and 6, including extending the temperature
> +       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
> +       will reveal whether they are compatible or not.
> +
> +0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
> +0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
> +       critical temperature protection
> +0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
> +0x552: only on EHF, vin4
> +0x558: only on EHF, vin4 high limit
> +0x559: only on EHF, vin4 low limit
> +0x6b:  only on DHG, SYS fan critical temperature
> +0x6c:  only on DHG, CPU fan0 critical temperature
> +0x6d:  only on DHG, AUX fan critical temperature
> +0x6e:  only on DHG, CPU fan1 critical temperature
> +
> +0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
> +       Register" for the DHG
> +
> +The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
> +the ICH8 southbridge gets that data via PECI from the DHG, so that the
> +southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
>   
> ------------------------------------------------------------------------
>
> /*
>     w83627ehf - Driver for the hardware monitoring functionality of
>                 the Winbond W83627EHF Super-I/O chip
>     Copyright (C) 2005  Jean Delvare <khali at linux-fr.org>
>     Copyright (C) 2006  Yuan Mu (Winbond),
>                         Rudolf Marek <r.marek at assembler.cz>
>                         David Hubbard <david.c.hubbard at gmail.com>
>
>     Shamelessly ripped from the w83627hf driver
>     Copyright (C) 2003  Mark Studebaker
>
>     Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
>     in testing and debugging this driver.
>
>     This driver also supports the W83627EHG, which is the lead-free
>     version of the W83627EHF.
>
>     This program is free software; you can redistribute it and/or modify
>     it under the terms of the GNU General Public License as published by
>     the Free Software Foundation; either version 2 of the License, or
>     (at your option) any later version.
>
>     This program is distributed in the hope that it will be useful,
>     but WITHOUT ANY WARRANTY; without even the implied warranty of
>     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>     GNU General Public License for more details.
>
>     You should have received a copy of the GNU General Public License
>     along with this program; if not, write to the Free Software
>     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>
>
>     Supports the following chips:
>
>     Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
>     w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
>                                                0x8860 0xa1
>     w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
> */
>
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/slab.h>
> #include <linux/i2c.h>
> #include <linux/i2c-isa.h>
> #include <linux/hwmon.h>
> #include <linux/hwmon-sysfs.h>
> #include <linux/err.h>
> #include <linux/mutex.h>
> #include <asm/io.h>
> #include "lm75.h"
>
> /* The actual ISA address is read from Super-I/O configuration space */
> static unsigned short address;
>
> /*
>  * Super-I/O constants and functions
>  */
>
> /*
>  * The three following globals are initialized in w83627ehf_find(), before
>  * the i2c-isa device is created. Otherwise, they could be stored in
>  * w83627ehf_data. This is ugly, but necessary. and when the driver is next
>  * updated to become a platform driver, the globals will disappear.
>  */
> static int REG;		/* The register to read/write */
> static int VAL;		/* The value to read/write */
> /* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
>  * value is also used in w83627ehf_detect() to export a device name in sysfs
>  * (e.g. w83627ehf or w83627dhg) */
> static int w83627ehf_num_in;
>
> #define W83627EHF_LD_HWM	0x0b
>
> #define SIO_REG_LDSEL		0x07	/* Logical device select */
> #define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
> #define SIO_REG_ENABLE		0x30	/* Logical device enable */
> #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
>
> #define SIO_W83627EHF_ID	0x8850
> #define SIO_W83627EHG_ID	0x8860
> #define SIO_W83627DHG_ID	0xa020
> #define SIO_ID_MASK		0xFFF0
>
> static inline void
> superio_outb(int reg, int val)
> {
> 	outb(reg, REG);
> 	outb(val, VAL);
> }
>
> static inline int
> superio_inb(int reg)
> {
> 	outb(reg, REG);
> 	return inb(VAL);
> }
>
> static inline void
> superio_select(int ld)
> {
> 	outb(SIO_REG_LDSEL, REG);
> 	outb(ld, VAL);
> }
>
> static inline void
> superio_enter(void)
> {
> 	outb(0x87, REG);
> 	outb(0x87, REG);
> }
>
> static inline void
> superio_exit(void)
> {
> 	outb(0x02, REG);
> 	outb(0x02, VAL);
> }
>
> /*
>  * ISA constants
>  */
>
> #define REGION_ALIGNMENT	~7
> #define REGION_OFFSET		5
> #define REGION_LENGTH		2
> #define ADDR_REG_OFFSET		5
> #define DATA_REG_OFFSET		6
>
> #define W83627EHF_REG_BANK		0x4E
> #define W83627EHF_REG_CONFIG		0x40
>
> /* Not currently used:
>  * REG_MAN_ID has the value 0x5ca3 for all supported chips.
>  * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
>  * REG_MAN_ID is at port 0x4f
>  * REG_CHIP_ID is at port 0x58 */
>
> 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
> static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
> static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
> static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
> static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
>
> /* Fan clock dividers are spread over the following five registers */
> #define W83627EHF_REG_FANDIV1		0x47
> #define W83627EHF_REG_FANDIV2		0x4B
> #define W83627EHF_REG_VBAT		0x5D
> #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
>
> /* SmartFan registers */
> /* DC or PWM output fan configuration */
> static const u8 W83627EHF_REG_PWM_ENABLE[] = {
> 	0x04,			/* SYS FAN0 output mode and PWM mode */
> 	0x04,			/* CPU FAN0 output mode and PWM mode */
> 	0x12,			/* AUX FAN mode */
> 	0x62,			/* CPU fan1 mode */
> };
>
> static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
> static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
>
> /* FAN Duty Cycle, be used to control */
> static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
> static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
> static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
>
>
> /* Advanced Fan control, some values are common for all fans */
> static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
> static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
>
> /*
>  * Conversions
>  */
>
> /* 1 is PWM mode, output in ms */
> static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
> {
> 	return mode ? 100 * reg : 400 * reg;
> }
>
> static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
> {
> 	return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
> 						(msec + 200) / 400), 1, 255);
> }
>
> static inline unsigned int
> fan_from_reg(u8 reg, unsigned int div)
> {
> 	if (reg = 0 || reg = 255)
> 		return 0;
> 	return 1350000U / (reg * div);
> }
>
> static inline unsigned int
> div_from_reg(u8 reg)
> {
> 	return 1 << reg;
> }
>
> static inline int
> temp1_from_reg(s8 reg)
> {
> 	return reg * 1000;
> }
>
> static inline s8
> temp1_to_reg(int temp, int min, int max)
> {
> 	if (temp <= min)
> 		return min / 1000;
> 	if (temp >= max)
> 		return max / 1000;
> 	if (temp < 0)
> 		return (temp - 500) / 1000;
> 	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
>  */
>
> struct w83627ehf_data {
> 	struct i2c_client client;
> 	struct class_device *class_dev;
> 	struct mutex lock;
>
> 	struct mutex update_lock;
> 	char valid;		/* !=0 if following fields are valid */
> 	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];
> 	u8 has_fan;		/* some fan inputs can be disabled */
> 	s8 temp1;
> 	s8 temp1_max;
> 	s8 temp1_max_hyst;
> 	s16 temp[2];
> 	s16 temp_max[2];
> 	s16 temp_max_hyst[2];
> 	u32 alarms;
>
> 	u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
> 	u8 pwm_enable[4]; /* 1->manual
> 			     2->thermal cruise (also called SmartFan I) */
> 	u8 pwm[4];
> 	u8 target_temp[4];
> 	u8 tolerance[4];
>
> 	u8 fan_min_output[4]; /* minimum fan speed */
> 	u8 fan_stop_time[4];
> };
>
> static inline int is_word_sized(u16 reg)
> {
> 	return (((reg & 0xff00) = 0x100
> 	      || (reg & 0xff00) = 0x200)
> 	     && ((reg & 0x00ff) = 0x50
> 	      || (reg & 0x00ff) = 0x53
> 	      || (reg & 0x00ff) = 0x55));
> }
>
> /* We assume that the default bank is 0, thus the following two functions do
>    nothing for registers which live in bank 0. For others, they respectively
>    set the bank register to the correct value (before the register is
>    accessed), and back to 0 (afterwards). */
> static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
> {
> 	if (reg & 0xff00) {
> 		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
> 		outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
> 	}
> }
>
> static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
> {
> 	if (reg & 0xff00) {
> 		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
> 		outb_p(0, client->addr + DATA_REG_OFFSET);
> 	}
> }
>
> static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
> {
> 	struct w83627ehf_data *data = i2c_get_clientdata(client);
> 	int res, word_sized = is_word_sized(reg);
>
> 	mutex_lock(&data->lock);
>
> 	w83627ehf_set_bank(client, reg);
> 	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
> 	res = inb_p(client->addr + DATA_REG_OFFSET);
> 	if (word_sized) {
> 		outb_p((reg & 0xff) + 1,
> 		       client->addr + ADDR_REG_OFFSET);
> 		res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
> 	}
> 	w83627ehf_reset_bank(client, reg);
>
> 	mutex_unlock(&data->lock);
>
> 	return res;
> }
>
> static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
> {
> 	struct w83627ehf_data *data = i2c_get_clientdata(client);
> 	int word_sized = is_word_sized(reg);
>
> 	mutex_lock(&data->lock);
>
> 	w83627ehf_set_bank(client, reg);
> 	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
> 	if (word_sized) {
> 		outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
> 		outb_p((reg & 0xff) + 1,
> 		       client->addr + ADDR_REG_OFFSET);
> 	}
> 	outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
> 	w83627ehf_reset_bank(client, reg);
>
> 	mutex_unlock(&data->lock);
> 	return 0;
> }
>
> /* This function assumes that the caller holds data->update_lock */
> static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
> {
> 	struct w83627ehf_data *data = i2c_get_clientdata(client);
> 	u8 reg;
>
> 	switch (nr) {
> 	case 0:
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
> 		    | ((data->fan_div[0] & 0x03) << 4);
> 		/* fan5 input control bit is write only, compute the value */
> 		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
> 		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
> 		    | ((data->fan_div[0] & 0x04) << 3);
> 		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
> 		break;
> 	case 1:
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
> 		    | ((data->fan_div[1] & 0x03) << 6);
> 		/* fan5 input control bit is write only, compute the value */
> 		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
> 		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
> 		    | ((data->fan_div[1] & 0x04) << 4);
> 		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
> 		break;
> 	case 2:
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
> 		    | ((data->fan_div[2] & 0x03) << 6);
> 		w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
> 		    | ((data->fan_div[2] & 0x04) << 5);
> 		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
> 		break;
> 	case 3:
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
> 		    | (data->fan_div[3] & 0x03);
> 		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
> 		    | ((data->fan_div[3] & 0x04) << 5);
> 		w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
> 		break;
> 	case 4:
> 		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
> 		    | ((data->fan_div[4] & 0x03) << 3)
> 		    | ((data->fan_div[4] & 0x04) << 5);
> 		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
> 		break;
> 	}
> }
>
> static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
> {
> 	struct i2c_client *client = to_i2c_client(dev);
> 	struct w83627ehf_data *data = i2c_get_clientdata(client);
> 	int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
> 	int i;
>
> 	mutex_lock(&data->update_lock);
>
> 	if (time_after(jiffies, data->last_updated + HZ)
> 	 || !data->valid) {
> 		/* Fan clock dividers */
> 		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
> 		data->fan_div[0] = (i >> 4) & 0x03;
> 		data->fan_div[1] = (i >> 6) & 0x03;
> 		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
> 		data->fan_div[2] = (i >> 6) & 0x03;
> 		i = w83627ehf_read_value(client, W83627EHF_REG_VBAT);
> 		data->fan_div[0] |= (i >> 3) & 0x04;
> 		data->fan_div[1] |= (i >> 4) & 0x04;
> 		data->fan_div[2] |= (i >> 5) & 0x04;
> 		if (data->has_fan & ((1 << 3) | (1 << 4))) {
> 			i = w83627ehf_read_value(client, W83627EHF_REG_DIODE);
> 			data->fan_div[3] = i & 0x03;
> 			data->fan_div[4] = ((i >> 2) & 0x03)
> 					 | ((i >> 5) & 0x04);
> 		}
> 		if (data->has_fan & (1 << 3)) {
> 			i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT);
> 			data->fan_div[3] |= (i >> 5) & 0x04;
> 		}
>
> 		/* Measured voltages and limits */
> 		for (i = 0; i < w83627ehf_num_in; 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)))
> 				continue;
>
> 			data->fan[i] = w83627ehf_read_value(client,
> 				       W83627EHF_REG_FAN[i]);
> 			data->fan_min[i] = w83627ehf_read_value(client,
> 					   W83627EHF_REG_FAN_MIN[i]);
>
> 			/* If we failed to measure the fan speed and clock
> 			   divider can be increased, let's try that for next
> 			   time */
> 			if (data->fan[i] = 0xff
> 			 && data->fan_div[i] < 0x07) {
> 			 	dev_dbg(&client->dev, "Increasing fan %d "
> 					"clock divider from %u to %u\n",
> 					i, div_from_reg(data->fan_div[i]),
> 					div_from_reg(data->fan_div[i] + 1));
> 				data->fan_div[i]++;
> 				w83627ehf_write_fan_div(client, i);
> 				/* Preserve min limit if possible */
> 				if (data->fan_min[i] >= 2
> 				 && data->fan_min[i] != 255)
> 					w83627ehf_write_value(client,
> 						W83627EHF_REG_FAN_MIN[i],
> 						(data->fan_min[i] /= 2));
> 			}
> 		}
>
> 		for (i = 0; i < 4; i++) {
> 			/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
> 			if (i != 1) {
> 				pwmcfg = w83627ehf_read_value(client,
> 						W83627EHF_REG_PWM_ENABLE[i]);
> 				tolerance = w83627ehf_read_value(client,
> 						W83627EHF_REG_TOLERANCE[i]);
> 			}
> 			data->pwm_mode[i] > 				((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
> 				? 0 : 1;
> 			data->pwm_enable[i] > 					((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
> 						& 3) + 1;
> 			data->pwm[i] = w83627ehf_read_value(client,
> 						W83627EHF_REG_PWM[i]);
> 			data->fan_min_output[i] = w83627ehf_read_value(client,
> 						W83627EHF_REG_FAN_MIN_OUTPUT[i]);
> 			data->fan_stop_time[i] = w83627ehf_read_value(client,
> 						W83627EHF_REG_FAN_STOP_TIME[i]);
> 			data->target_temp[i] > 				w83627ehf_read_value(client,
> 					W83627EHF_REG_TARGET[i]) &
> 					(data->pwm_mode[i] = 1 ? 0x7f : 0xff);
> 			data->tolerance[i] = (tolerance >> (i = 1 ? 4 : 0))
> 									& 0x0f;
> 		}
>
> 		/* Measured temperatures and limits */
> 		data->temp1 = w83627ehf_read_value(client,
> 			      W83627EHF_REG_TEMP1);
> 		data->temp1_max = w83627ehf_read_value(client,
> 				  W83627EHF_REG_TEMP1_OVER);
> 		data->temp1_max_hyst = w83627ehf_read_value(client,
> 				       W83627EHF_REG_TEMP1_HYST);
> 		for (i = 0; i < 2; i++) {
> 			data->temp[i] = w83627ehf_read_value(client,
> 					W83627EHF_REG_TEMP[i]);
> 			data->temp_max[i] = w83627ehf_read_value(client,
> 					    W83627EHF_REG_TEMP_OVER[i]);
> 			data->temp_max_hyst[i] = w83627ehf_read_value(client,
> 						 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;
> 	}
>
> 	mutex_unlock(&data->update_lock);
> 	return data;
> }
>
> /*
>  * 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 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),
> 	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_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),
>        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 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, "%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
> show_fan_div(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", div_from_reg(data->fan_div[nr]));
> }
>
> static ssize_t
> store_fan_min(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;
> 	unsigned int val = simple_strtoul(buf, NULL, 10);
> 	unsigned int reg;
> 	u8 new_div;
>
> 	mutex_lock(&data->update_lock);
> 	if (!val) {
> 		/* No min limit, alarm disabled */
> 		data->fan_min[nr] = 255;
> 		new_div = data->fan_div[nr]; /* No change */
> 		dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
> 	} else if ((reg = 1350000U / val) >= 128 * 255) {
> 		/* Speed below this value cannot possibly be represented,
> 		   even with the highest divider (128) */
> 		data->fan_min[nr] = 254;
> 		new_div = 7; /* 128 = (1 << 7) */
> 		dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
> 			 "minimum\n", nr + 1, val, fan_from_reg(254, 128));
> 	} else if (!reg) {
> 		/* Speed above this value cannot possibly be represented,
> 		   even with the lowest divider (1) */
> 		data->fan_min[nr] = 1;
> 		new_div = 0; /* 1 = (1 << 0) */
> 		dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
> 			 "maximum\n", nr + 1, val, fan_from_reg(1, 1));
> 	} else {
> 		/* Automatically pick the best divider, i.e. the one such
> 		   that the min limit will correspond to a register value
> 		   in the 96..192 range */
> 		new_div = 0;
> 		while (reg > 192 && new_div < 7) {
> 			reg >>= 1;
> 			new_div++;
> 		}
> 		data->fan_min[nr] = reg;
> 	}
>
> 	/* Write both the fan clock divider (if it changed) and the new
> 	   fan min (unconditionally) */
> 	if (new_div != data->fan_div[nr]) {
> 		if (new_div > data->fan_div[nr])
> 			data->fan[nr] >>= (data->fan_div[nr] - new_div);
> 		else
> 			data->fan[nr] <<= (new_div - data->fan_div[nr]);
>
> 		dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
> 			nr + 1, div_from_reg(data->fan_div[nr]),
> 			div_from_reg(new_div));
> 		data->fan_div[nr] = new_div;
> 		w83627ehf_write_fan_div(client, nr);
> 	}
> 	w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
> 			      data->fan_min[nr]);
> 	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_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),
> 	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_IRUGO, show_fan_div, NULL, 0),
> 	SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
> 	SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
> 	SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
> 	SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
> };
>
> #define show_temp1_reg(reg) \
> static ssize_t \
> show_##reg(struct device *dev, struct device_attribute *attr, \
> 	   char *buf) \
> { \
> 	struct w83627ehf_data *data = w83627ehf_update_device(dev); \
> 	return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
> }
> show_temp1_reg(temp1);
> show_temp1_reg(temp1_max);
> show_temp1_reg(temp1_max_hyst);
>
> #define store_temp1_reg(REG, reg) \
> static ssize_t \
> store_temp1_##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); \
> 	u32 val = simple_strtoul(buf, NULL, 10); \
>  \
> 	mutex_lock(&data->update_lock); \
> 	data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
> 	w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
> 			      data->temp1_##reg); \
> 	mutex_unlock(&data->update_lock); \
> 	return count; \
> }
> store_temp1_reg(OVER, max);
> store_temp1_reg(HYST, max_hyst);
>
> #define show_temp_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, "%d\n", \
> 		       LM75_TEMP_FROM_REG(data->reg[nr])); \
> }
> show_temp_reg(temp);
> show_temp_reg(temp_max);
> show_temp_reg(temp_max_hyst);
>
> #define store_temp_reg(REG, reg) \
> static ssize_t \
> store_##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->reg[nr] = LM75_TEMP_TO_REG(val); \
> 	w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
> 			      data->reg[nr]); \
> 	mutex_unlock(&data->update_lock); \
> 	return count; \
> }
> store_temp_reg(OVER, temp_max);
> store_temp_reg(HYST, temp_max_hyst);
>
> static struct sensor_device_attribute sda_temp[] = {
> 	SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
> 	SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
> 	SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
> 	SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
> 		    store_temp1_max, 0),
> 	SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
> 		    store_temp_max, 0),
> 	SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
> 		    store_temp_max, 1),
> 	SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
> 		    store_temp1_max_hyst, 0),
> 	SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
> 		    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),
> };
>
> #define show_pwm_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, "%d\n", data->reg[nr]); \
> }
>
> show_pwm_reg(pwm_mode)
> show_pwm_reg(pwm_enable)
> show_pwm_reg(pwm)
>
> static ssize_t
> store_pwm_mode(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);
> 	u16 reg;
>
> 	if (val > 1)
> 		return -EINVAL;
> 	mutex_lock(&data->update_lock);
> 	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
> 	data->pwm_mode[nr] = val;
> 	reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
> 	if (!val)
> 		reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
> 	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
> 	mutex_unlock(&data->update_lock);
> 	return count;
> }
>
> static ssize_t
> store_pwm(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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
>
> 	mutex_lock(&data->update_lock);
> 	data->pwm[nr] = val;
> 	w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
> 	mutex_unlock(&data->update_lock);
> 	return count;
> }
>
> static ssize_t
> store_pwm_enable(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);
> 	u16 reg;
>
> 	if (!val || (val > 2))	/* only modes 1 and 2 are supported */
> 		return -EINVAL;
> 	mutex_lock(&data->update_lock);
> 	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
> 	data->pwm_enable[nr] = val;
> 	reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
> 	reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
> 	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
> 	mutex_unlock(&data->update_lock);
> 	return count;
> }
>
>
> #define show_tol_temp(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, "%d\n", temp1_from_reg(data->reg[nr])); \
> }
>
> show_tol_temp(tolerance)
> show_tol_temp(target_temp)
>
> static ssize_t
> store_target_temp(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;
> 	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
>
> 	mutex_lock(&data->update_lock);
> 	data->target_temp[nr] = val;
> 	w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
> 	mutex_unlock(&data->update_lock);
> 	return count;
> }
>
> static ssize_t
> store_tolerance(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;
> 	u16 reg;
> 	/* Limit the temp to 0C - 15C */
> 	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
>
> 	mutex_lock(&data->update_lock);
> 	reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
> 	data->tolerance[nr] = val;
> 	if (nr = 1)
> 		reg = (reg & 0x0f) | (val << 4);
> 	else
> 		reg = (reg & 0xf0) | val;
> 	w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
> 	mutex_unlock(&data->update_lock);
> 	return count;
> }
>
> static struct sensor_device_attribute sda_pwm[] = {
> 	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),
> 	SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
> };
>
> static struct sensor_device_attribute sda_pwm_mode[] = {
> 	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
> 		    store_pwm_mode, 0),
> 	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
> 		    store_pwm_mode, 1),
> 	SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
> 		    store_pwm_mode, 2),
> 	SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
> 		    store_pwm_mode, 3),
> };
>
> static struct sensor_device_attribute sda_pwm_enable[] = {
> 	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
> 		    store_pwm_enable, 0),
> 	SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
> 		    store_pwm_enable, 1),
> 	SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
> 		    store_pwm_enable, 2),
> 	SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
> 		    store_pwm_enable, 3),
> };
>
> static struct sensor_device_attribute sda_target_temp[] = {
> 	SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
> 		    store_target_temp, 0),
> 	SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
> 		    store_target_temp, 1),
> 	SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
> 		    store_target_temp, 2),
> 	SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
> 		    store_target_temp, 3),
> };
>
> static struct sensor_device_attribute sda_tolerance[] = {
> 	SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
> 		    store_tolerance, 0),
> 	SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
> 		    store_tolerance, 1),
> 	SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
> 		    store_tolerance, 2),
> 	SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
> 		    store_tolerance, 3),
> };
>
> /* Smart Fan registers */
>
> #define fan_functions(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, "%d\n", data->reg[nr]); \
> }\
> static ssize_t \
> store_##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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
> 	mutex_lock(&data->update_lock); \
> 	data->reg[nr] = val; \
> 	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr],  val); \
> 	mutex_unlock(&data->update_lock); \
> 	return count; \
> }
>
> fan_functions(fan_min_output, FAN_MIN_OUTPUT)
>
> #define fan_time_functions(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, "%d\n", \
> 			step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
> } \
> \
> static ssize_t \
> store_##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; \
> 	u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
> 					data->pwm_mode[nr]); \
> 	mutex_lock(&data->update_lock); \
> 	data->reg[nr] = val; \
> 	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
> 	mutex_unlock(&data->update_lock); \
> 	return count; \
> } \
>
> fan_time_functions(fan_stop_time, FAN_STOP_TIME)
>
>
> static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
> 	SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
> 		    store_fan_stop_time, 3),
> 	SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
> 		    store_fan_min_output, 3),
> };
>
> static struct sensor_device_attribute sda_sf3_arrays[] = {
> 	SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
> 		    store_fan_stop_time, 0),
> 	SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
> 		    store_fan_stop_time, 1),
> 	SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
> 		    store_fan_stop_time, 2),
> 	SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
> 		    store_fan_min_output, 0),
> 	SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
> 		    store_fan_min_output, 1),
> 	SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
> 		    store_fan_min_output, 2),
> };
>
> /*
>  * Driver and client management
>  */
>
> static void w83627ehf_device_remove_files(struct device *dev)
> {
> 	/* some entries in the following arrays may not have been used in
> 	 * device_create_file(), but device_remove_file() will ignore them */
> 	int i;
>
> 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
> 		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
> 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
> 		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
> 	for (i = 0; i < w83627ehf_num_in; i++) {
> 		device_remove_file(dev, &sda_in_input[i].dev_attr);
> 		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
> 		device_remove_file(dev, &sda_in_min[i].dev_attr);
> 		device_remove_file(dev, &sda_in_max[i].dev_attr);
> 	}
> 	for (i = 0; i < 5; i++) {
> 		device_remove_file(dev, &sda_fan_input[i].dev_attr);
> 		device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
> 		device_remove_file(dev, &sda_fan_div[i].dev_attr);
> 		device_remove_file(dev, &sda_fan_min[i].dev_attr);
> 	}
> 	for (i = 0; i < 4; i++) {
> 		device_remove_file(dev, &sda_pwm[i].dev_attr);
> 		device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
> 		device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
> 		device_remove_file(dev, &sda_target_temp[i].dev_attr);
> 		device_remove_file(dev, &sda_tolerance[i].dev_attr);
> 	}
> 	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
> 		device_remove_file(dev, &sda_temp[i].dev_attr);
> }
>
> static struct i2c_driver w83627ehf_driver;
>
> static void w83627ehf_init_client(struct i2c_client *client)
> {
> 	int i;
> 	u8 tmp;
>
> 	/* Start monitoring is needed */
> 	tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
> 	if (!(tmp & 0x01))
> 		w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
> 				      tmp | 0x01);
>
> 	/* Enable temp2 and temp3 if needed */
> 	for (i = 0; i < 2; i++) {
> 		tmp = w83627ehf_read_value(client,
> 					   W83627EHF_REG_TEMP_CONFIG[i]);
> 		if (tmp & 0x01)
> 			w83627ehf_write_value(client,
> 					      W83627EHF_REG_TEMP_CONFIG[i],
> 					      tmp & 0xfe);
> 	}
> }
>
> static int w83627ehf_detect(struct i2c_adapter *adapter)
> {
> 	struct i2c_client *client;
> 	struct w83627ehf_data *data;
> 	struct device *dev;
> 	u8 fan4pin, fan5pin;
> 	int i, err = 0;
>
> 	if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
> 	                    w83627ehf_driver.driver.name)) {
> 		err = -EBUSY;
> 		goto exit;
> 	}
>
> 	if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
> 		err = -ENOMEM;
> 		goto exit_release;
> 	}
>
> 	client = &data->client;
> 	i2c_set_clientdata(client, data);
> 	client->addr = address;
> 	mutex_init(&data->lock);
> 	client->adapter = adapter;
> 	client->driver = &w83627ehf_driver;
> 	client->flags = 0;
> 	dev = &client->dev;
>
> 	if (w83627ehf_num_in = 9)
> 		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
> 	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
> 		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
>
> 	data->valid = 0;
> 	mutex_init(&data->update_lock);
>
> 	/* Tell the i2c layer a new client has arrived */
> 	if ((err = i2c_attach_client(client)))
> 		goto exit_free;
>
> 	/* Initialize the chip */
> 	w83627ehf_init_client(client);
>
> 	/* A few vars need to be filled upon startup */
> 	for (i = 0; i < 5; i++)
> 		data->fan_min[i] = w83627ehf_read_value(client,
> 				   W83627EHF_REG_FAN_MIN[i]);
>
> 	/* fan4 and fan5 share some pins with the GPIO and serial flash */
>
> 	superio_enter();
> 	fan5pin = superio_inb(0x24) & 0x2;
> 	fan4pin = superio_inb(0x29) & 0x6;
> 	superio_exit();
>
> 	/* It looks like fan4 and fan5 pins can be alternatively used
> 	   as fan on/off switches, but fan5 control is write only :/
> 	   We assume that if the serial interface is disabled, designers
> 	   connected fan5 as input unless they are emitting log 1, which
> 	   is not the default. */
>
> 	data->has_fan = 0x07; /* fan1, fan2 and fan3 */
> 	i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
> 	if ((i & (1 << 2)) && (!fan4pin))
> 		data->has_fan |= (1 << 3);
> 	if (!(i & (1 << 1)) && (!fan5pin))
> 		data->has_fan |= (1 << 4);
>
> 	/* Register sysfs hooks */
>   	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
> 		if ((err = device_create_file(dev,
> 			&sda_sf3_arrays[i].dev_attr)))
> 			goto exit_remove;
>
> 	/* if fan4 is enabled create the sf3 files for it */
> 	if (data->has_fan & (1 << 3))
> 		for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
> 			if ((err = device_create_file(dev,
> 				&sda_sf3_arrays_fan4[i].dev_attr)))
> 				goto exit_remove;
> 		}
>
> 	for (i = 0; i < w83627ehf_num_in; i++)
> 		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
> 			|| (err = device_create_file(dev,
> 				&sda_in_alarm[i].dev_attr))
> 			|| (err = device_create_file(dev,
> 				&sda_in_min[i].dev_attr))
> 			|| (err = device_create_file(dev,
> 				&sda_in_max[i].dev_attr)))
> 			goto exit_remove;
>
> 	for (i = 0; i < 5; i++) {
> 		if (data->has_fan & (1 << i)) {
> 			if ((err = device_create_file(dev,
> 					&sda_fan_input[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_fan_alarm[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_fan_div[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_fan_min[i].dev_attr)))
> 				goto exit_remove;
> 			if (i < 4 && /* w83627ehf only has 4 pwm */
> 				((err = device_create_file(dev,
> 					&sda_pwm[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_pwm_mode[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_pwm_enable[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_target_temp[i].dev_attr))
> 				|| (err = device_create_file(dev,
> 					&sda_tolerance[i].dev_attr))))
> 				goto exit_remove;
> 		}
> 	}
>
> 	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
> 		if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
> 			goto exit_remove;
>
> 	data->class_dev = hwmon_device_register(dev);
> 	if (IS_ERR(data->class_dev)) {
> 		err = PTR_ERR(data->class_dev);
> 		goto exit_remove;
> 	}
>
> 	return 0;
>
> exit_remove:
> 	w83627ehf_device_remove_files(dev);
> 	i2c_detach_client(client);
> exit_free:
> 	kfree(data);
> exit_release:
> 	release_region(address + REGION_OFFSET, REGION_LENGTH);
> exit:
> 	return err;
> }
>
> static int w83627ehf_detach_client(struct i2c_client *client)
> {
> 	struct w83627ehf_data *data = i2c_get_clientdata(client);
> 	int err;
>
> 	hwmon_device_unregister(data->class_dev);
> 	w83627ehf_device_remove_files(&client->dev);
>
> 	if ((err = i2c_detach_client(client)))
> 		return err;
> 	release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
> 	kfree(data);
>
> 	return 0;
> }
>
> static struct i2c_driver w83627ehf_driver = {
> 	.driver = {
> 		.owner	= THIS_MODULE,
> 		.name	= "w83627ehf",
> 	},
> 	.attach_adapter	= w83627ehf_detect,
> 	.detach_client	= w83627ehf_detach_client,
> };
>
> static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
> {
> 	u16 val;
>
> 	REG = sioaddr;
> 	VAL = sioaddr + 1;
> 	superio_enter();
>
> 	val = (superio_inb(SIO_REG_DEVID) << 8)
> 	    | superio_inb(SIO_REG_DEVID + 1);
> 	switch (val & SIO_ID_MASK) {
> 	case SIO_W83627DHG_ID:
> 		w83627ehf_num_in = 9;
> 		break;
> 	case SIO_W83627EHF_ID:
> 	case SIO_W83627EHG_ID:
> 		w83627ehf_num_in = 10;
> 		break;
> 	default:
> 		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
> 			val);
> 		superio_exit();
> 		return -ENODEV;
> 	}
>
> 	superio_select(W83627EHF_LD_HWM);
> 	val = (superio_inb(SIO_REG_ADDR) << 8)
> 	    | superio_inb(SIO_REG_ADDR + 1);
> 	*addr = val & REGION_ALIGNMENT;
> 	if (*addr = 0) {
> 		superio_exit();
> 		return -ENODEV;
> 	}
>
> 	/* Activate logical device if needed */
> 	val = superio_inb(SIO_REG_ENABLE);
> 	if (!(val & 0x01))
> 		superio_outb(SIO_REG_ENABLE, val | 0x01);
>
> 	superio_exit();
> 	return 0;
> }
>
> static int __init sensors_w83627ehf_init(void)
> {
> 	if (w83627ehf_find(0x2e, &address)
> 	 && w83627ehf_find(0x4e, &address))
> 		return -ENODEV;
>
> 	return i2c_isa_add_driver(&w83627ehf_driver);
> }
>
> static void __exit sensors_w83627ehf_exit(void)
> {
> 	i2c_isa_del_driver(&w83627ehf_driver);
> }
>
> MODULE_AUTHOR("Jean Delvare <khali at linux-fr.org>");
> MODULE_DESCRIPTION("W83627EHF driver");
> MODULE_LICENSE("GPL");
>
> module_init(sensors_w83627ehf_init);
> module_exit(sensors_w83627ehf_exit);
>   
> ------------------------------------------------------------------------
>
> #!/bin/bash
>
> if [ $UID != 0 ]; then
>     echo "This script tests the w83627ehf module. It must be run as root."
>     exit 1
> fi
>
> echo "* WARNING: This will run your system fans through many possible"
> echo "           combinations. There is a possibility your system will"
> echo "           overheat. Use this script at your own risk!"
>
> # locate the w83627ehf in the sysfs
> SYS="`awk '{if (\$3=\"sysfs\") {print \$2;exit}}' /etc/mtab`"
> #'
> found_w83627ehf=""
> if [ -d $SYS/class/hwmon ]; then
>     for a in $SYS/class/hwmon/*; do
>         if [ "`cat $a/device/name`" = "w83627ehf" ]; then
>             found_w83627ehf="$a"
> 	fi
>     done
> fi
> if [ -z "$found_w83627ehf" ]; then
>     echo "No w83627ehf driver in system. Loading module."
>     modprobe w83627ehf || exit 1
> else
>     if lsmod | grep -q w83627ehf; then
>         if modprobe -r w83627ehf && modprobe w83627ehf; then
>             :
>         else
>             echo "Module w83627ehf: modprobe and unload failed."
>             exit 1
>         fi
>     fi
> fi
>
> # check sysfs entries, permissions
> hw=${found_w83627ehf/$SYS\/class\/hwmon\//}
> #`
> cd "$found_w83627ehf/device"
> ls -l | cut -c-28,42- | (
>     d=0
>     check_line()
>     {
>         read a || exit 1
>         if [ "$a" != "$1" ]; then
>             if [ $d = 0 ]; then
>                 echo "Permissions or files mismatched ($found_w83627ehf/device)"
>             fi
>             echo "$a"
>             d=1
>         fi
>     }
>     check_line_skip()
>     {
>         while true; do
>             if ! read a; then
>                 if [ $d = 0 ]; then
>                     echo "xPermissions or files mismatched ($found_w83627ehf/device)"
>                 fi
>                 d=1
>                 break
>             fi
>             if [ "$a" = "$1" ]; then
>                 break
>             fi
>         done
>     }
>     check_line "total 0"
>     check_line "lrwxrwxrwx 1 root root    0 bus -> ../../../../bus/i2c"
>     l="driver -> ../../../../bus/i2c/drivers/w83627ehf"
>     check_line "lrwxrwxrwx 1 root root    0 $l"
>     check_line "-r--r--r-- 1 root root 4096 fan1_alarm"
>     check_line "-r--r--r-- 1 root root 4096 fan1_div"
>     check_line "-r--r--r-- 1 root root 4096 fan1_input"
>     check_line "-rw-r--r-- 1 root root 4096 fan1_min"
>     #check_line "-rw-r--r-- 1 root root 4096 fan1_min_output"
>     #check_line "-rw-r--r-- 1 root root 4096 fan1_stop_time"
>     check_line "-r--r--r-- 1 root root 4096 fan2_alarm"
>     check_line "-r--r--r-- 1 root root 4096 fan2_div"
>     check_line "-r--r--r-- 1 root root 4096 fan2_input"
>     #check_line "-rw-r--r-- 1 root root 4096 fan2_max_output"
>     check_line "-rw-r--r-- 1 root root 4096 fan2_min"
>     #check_line "-rw-r--r-- 1 root root 4096 fan2_min_output"
>     #check_line "-rw-r--r-- 1 root root 4096 fan2_step"
>     #check_line "-rw-r--r-- 1 root root 4096 fan2_stop_time"
>     check_line "-r--r--r-- 1 root root 4096 fan3_alarm"
>     check_line "-r--r--r-- 1 root root 4096 fan3_div"
>     check_line "-r--r--r-- 1 root root 4096 fan3_input"
>     check_line "-rw-r--r-- 1 root root 4096 fan3_min"
>     #check_line "-rw-r--r-- 1 root root 4096 fan3_min_output"
>     #check_line "-rw-r--r-- 1 root root 4096 fan3_stop_time"
>     # skips any fan4_*, fan5_* entries...this could be done better
>     # check_line "-r--r--r-- 1 root root 4096 fan4_alarm"
>     # check_line "-r--r--r-- 1 root root 4096 fan4_div"
>     # check_line "-r--r--r-- 1 root root 4096 fan4_input"
>     # check_line "-rw-r--r-- 1 root root 4096 fan4_max_output"
>     # check_line "-rw-r--r-- 1 root root 4096 fan4_min"
>     # check_line "-rw-r--r-- 1 root root 4096 fan4_min_output"
>     # check_line "-rw-r--r-- 1 root root 4096 fan4_step"
>     # check_line "-rw-r--r-- 1 root root 4096 fan4_stop_time"
>     #check_line "-rw-r--r-- 1 root root 4096 fan_step_down_time"
>     #check_line "-rw-r--r-- 1 root root 4096 fan_step_up_time"
>     l="hwmon:$hw -> ../../../../class/hwmon/$hw"
>     check_line_skip "lrwxrwxrwx 1 root root    0 $l"
>     check_line "-r--r--r-- 1 root root 4096 in0_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in0_input"
>     check_line "-rw-r--r-- 1 root root 4096 in0_max"
>     check_line "-rw-r--r-- 1 root root 4096 in0_min"
>     check_line "-r--r--r-- 1 root root 4096 in1_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in1_input"
>     check_line "-rw-r--r-- 1 root root 4096 in1_max"
>     check_line "-rw-r--r-- 1 root root 4096 in1_min"
>     check_line "-r--r--r-- 1 root root 4096 in2_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in2_input"
>     check_line "-rw-r--r-- 1 root root 4096 in2_max"
>     check_line "-rw-r--r-- 1 root root 4096 in2_min"
>     check_line "-r--r--r-- 1 root root 4096 in3_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in3_input"
>     check_line "-rw-r--r-- 1 root root 4096 in3_max"
>     check_line "-rw-r--r-- 1 root root 4096 in3_min"
>     check_line "-r--r--r-- 1 root root 4096 in4_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in4_input"
>     check_line "-rw-r--r-- 1 root root 4096 in4_max"
>     check_line "-rw-r--r-- 1 root root 4096 in4_min"
>     check_line "-r--r--r-- 1 root root 4096 in5_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in5_input"
>     check_line "-rw-r--r-- 1 root root 4096 in5_max"
>     check_line "-rw-r--r-- 1 root root 4096 in5_min"
>     check_line "-r--r--r-- 1 root root 4096 in6_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in6_input"
>     check_line "-rw-r--r-- 1 root root 4096 in6_max"
>     check_line "-rw-r--r-- 1 root root 4096 in6_min"
>     check_line "-r--r--r-- 1 root root 4096 in7_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in7_input"
>     check_line "-rw-r--r-- 1 root root 4096 in7_max"
>     check_line "-rw-r--r-- 1 root root 4096 in7_min"
>     check_line "-r--r--r-- 1 root root 4096 in8_alarm"
>     check_line "-r--r--r-- 1 root root 4096 in8_input"
>     check_line "-rw-r--r-- 1 root root 4096 in8_max"
>     check_line "-rw-r--r-- 1 root root 4096 in8_min"
>     # skips any in9_* entries...this could be done better
>     #check_line "-r--r--r-- 1 root root 4096 in9_alarm"
>     #check_line "-r--r--r-- 1 root root 4096 in9_input"
>     #check_line "-rw-r--r-- 1 root root 4096 in9_max"
>     #check_line "-rw-r--r-- 1 root root 4096 in9_min"
>     check_line_skip "-r--r--r-- 1 root root 4096 name"
>     check_line "drwxr-xr-x 2 root root    0 power"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_enable"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_min_output"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_mode"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_stop_time"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_target"
>     check_line "-rw-r--r-- 1 root root 4096 pwm1_tolerance"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_enable"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_min_output"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_mode"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_stop_time"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_target"
>     check_line "-rw-r--r-- 1 root root 4096 pwm2_tolerance"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_enable"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_min_output"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_mode"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_stop_time"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_target"
>     check_line "-rw-r--r-- 1 root root 4096 pwm3_tolerance"
>     # skips any pwm4_* entries...this could be done better
>     check_line_skip "-r--r--r-- 1 root root 4096 temp1_alarm"
>     check_line "-r--r--r-- 1 root root 4096 temp1_input"
>     check_line "-rw-r--r-- 1 root root 4096 temp1_max"
>     check_line "-rw-r--r-- 1 root root 4096 temp1_max_hyst"
>     #check_line "-rw-r--r-- 1 root root 4096 temp1_target"
>     #check_line "-rw-r--r-- 1 root root 4096 temp1_tolerance"
>     check_line "-r--r--r-- 1 root root 4096 temp2_alarm"
>     check_line "-r--r--r-- 1 root root 4096 temp2_input"
>     check_line "-rw-r--r-- 1 root root 4096 temp2_max"
>     check_line "-rw-r--r-- 1 root root 4096 temp2_max_hyst"
>     #check_line "-rw-r--r-- 1 root root 4096 temp2_target"
>     #check_line "-rw-r--r-- 1 root root 4096 temp2_tolerance"
>     check_line "-r--r--r-- 1 root root 4096 temp3_alarm"
>     check_line "-r--r--r-- 1 root root 4096 temp3_input"
>     check_line "-rw-r--r-- 1 root root 4096 temp3_max"
>     check_line "-rw-r--r-- 1 root root 4096 temp3_max_hyst"
>     #check_line "-rw-r--r-- 1 root root 4096 temp3_target"
>     #check_line "-rw-r--r-- 1 root root 4096 temp3_tolerance"
>     # skip any temp_4* entires...this could be done better
>     #check_line "-rw-r--r-- 1 root root 4096 temp4_target"
>     #check_line "-rw-r--r-- 1 root root 4096 temp4_tolerance"
>     check_line_skip "--w------- 1 root root 4096 uevent"
>     if read a; then echo "$a"; d=1; fi
>     [ $d != 0 ] && exit 1
>     :
>     ) || exit 1
>
> # input voltages
> # *** BUG? *** Are these formulas still being used? - dh
> #    label in0 "VCore"
> #    label in1 "VIN0"
> #    compute in1 @*(1+(56/10)),  @/(1+(56/10))
> #    label in2 "AVCC"
> #    label in3 "3VCC"
> #    label in4 "VIN1"
> #    label in5 "VIN2"
> #    label in6 "VIN3"
> #    compute in6 @*(1+(22/10)),  @/(1+(22/10))
> #    label in7 "VSB"
> #    label in8 "VBAT"
> #    label in9 "VIN4"
>
> check_voltage()
> {
>     V="`cat $1_input`" || exit 1
>     L="`cat $1_min`" || exit 1
>     H="`cat $1_max`" || exit 1
>     A="`cat $1_alarm`" || exit 1
>     if [ $V -lt $L -a $A = 0 ]; then
>         echo "Sensor   mV   min   max"
>         printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
>         echo "  Volts below min, alarm didn't go off."
>         exit 1
>     fi
>     if [ $V -gt $H -a $A = 0 ]; then
>         echo "Sensor   mV   min   max"
>         printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
>         echo "  Volts above max, alarm didn't go off."
>         exit 1
>     fi
>     if [ $V -gt $L -a $V -lt $H -a $A != 0 ]; then
>         echo "Sensor   mV   min   max"
>         printf "%-5s %5d %5d %5d\n" "$2" $(($V$3)) $(($L$3)) $(($H$3))
>         echo "  Volts between max and min but alarm went off."
>         exit 1
>     fi
> }
> check_voltage in0 "VCore" ""
> check_voltage in1 "+12V" "*66/10"
> check_voltage in2 "AVCC" ""
> check_voltage in3 "3VCC" ""
> check_voltage in4 "VIN1" ""
> check_voltage in5 "VIN2" ""
> check_voltage in6 "+5V" "*32/10"
> check_voltage in7 "VSB" ""
> check_voltage in8 "VBAT" ""
> check_voltage in9 "VIN4" ""
>
> check_temp()
> {
>     T="`cat $1_input`" || exit 1
>     if [ "$T" -lt "1" ]; then
>         echo "$1 too low: $T"
>         exit 1
>     fi
> }
> check_temp temp1
> check_temp temp2
> check_temp temp3
>
> test_smartfan1()
> {
> # fan$1_min_output is the minimum $2 will go to
> # why doesn't the fan stop?
> # maybe fan$1_min controls where fan$1_alarm is triggered
> #
> # SmartFan III items: fan2_step, fan2_max_output, fan4_step, fan4_max_output
>     echo ""
>     echo "test_smartfan1 \$1=\"$1\" \$2=\"$2\""
>
>     # ********* MUST VERIFY THAT fan_step_time IS CORRECT (PWM->100 ms, DC->400 ms)
>     # while $2 < 255 lower temp$1_target +/- temp$1_tolerance,
>     #     verify that $2 increases and temp$1_input decreases
>     # while $2 > fan$1_min_output increase temp$1_target +/- temp$1_tolerance,
>     #     verify that $2 decreases, stop if temp$1_input >= MIN(target+tolerance, 60000)
>     # restore temp$1_target
> }
> #test_smartfan1 3 pwm3
>
> #exit 0
>
> # build a mapping from pwm output to fan rpm input
> echo "Mapping fans...setting all PWMs to 100%"
> for a in pwm*_enable; do
>     p=${a/_enable/}
>     echo 1 > $a || exit 1
>     echo 255 > $p || exit 1
> done
>
> sleep 1
>
> read_fan_rpm()
> {
>     # take three readings. if they are not within 45% of each other, try again
>     retry=2
>     while [ $retry != 0 ]; do
>         sleep 2
>         t1="`cat fan$1_input`" || exit 1
>         sleep 2
>         t2="`cat fan$1_input`" || exit 1
>         sleep 2
>         t3="`cat fan$1_input`" || exit 1
>         l="$t1"
>         [ "$t2" -lt "$l" ] && l="$t2"
>         [ "$t3" -lt "$l" ] && l="$t3"
>         h="$t1"
>         [ "$t2" -gt "$h" ] && h="$t2"
>         [ "$t3" -gt "$h" ] && h="$t3"
>         if [ $l -gt 0 ]; then
>             h=$(($h-$l))
>             h=$(($h*10/$l))
>             [ "$h" -lt "4" ] && break
>         fi
>         retry=$(($retry-1))
>     done
> }
>
> for a in fan*_div; do
>     i=${a/fan/}
>     i=${i/_div/}
>     map[$i]=""
>     rpm="`cat fan${i}_input`" || exit 1
>     if [ "`cat $a`" = "128" -a "$rpm" = "0" ]; then
>         echo "    fan $i $rpm RPM: ignored. (Fan not installed?)"
>         continue
>     fi
>
>     # take the max of two readings
>     sleep 2
>     t="`cat fan${i}_input`" || exit 1
>     [ "$t" -gt "$rpm" ] && rpm="$t"
>
>     echo "    fan $i $rpm RPM:"
>     m=""
>     n=""
>     for b in pwm*_enable; do
>         echo 1 > ${b/_enable/}_mode || exit 1
>         echo 27 > ${b/_enable/} || exit 1
>         read_fan_rpm $i
>         echo 255 > ${b/_enable/} || exit 1
>         if [ $retry = 0 ]; then
>             echo "        ${b/_enable/} RPM highly variable? ignored."
>         else
>             d=$(($rpm-$l))
>             d=$(($d*4/$rpm))
>             if [ "$d" -gt "1" ]; then
>                 echo "        ${b/_enable/} $l RPM: connected."
>                 if [ -z "$m" ]; then
>                     m="${b/_enable/}"
>                 else
>                     n="${b/_enable/}"
>                 fi
>                 sleep 1
>             else
>                 echo "        ${b/_enable/} $l RPM"
>             fi
>         fi
>         if [ -n "$n" ]; then
>             echo "        Multiple PWMs mapped to fan? ignored."
>         else
>             map[$i]="$m"
>         fi
>     done
> done
>
> set_pwm_dc()
> {
>     echo $2 > $1_mode || exit 1
>     u="`cat $1_mode`" || exit 1
>     if [ "$u" != "$2" ]; then
>         [ "$2" = "0" ] && echo -e "\nFailure: could not set PWM mode."
>         [ "$2" != "0" ] && echo -e "\nFailure: could not set DC mode."
>         exit 1
>     fi
> }
>
> for a in fan*_div; do
>     i=${a/fan/}
>     i=${i/_div/}
>     [ -z "${map[$i]}" ] && continue
>     echo -n "Testing fan $i - ${map[$i]} ..."
>     # 1. test clock divider auto-scaling (high RPM and low RPM)
>     mún${i}_min
>     echo 1 > $m || exit 1
>     u="`cat $m`" || exit 1
>     v="`cat $a`" || exit 1
>     if [ "$u" -lt "41" -o "$v" != "128" ]; then
>         echo -e "\nFailure: $m = `cat $m` (not 41)  $a = `cat $a` (not 128)"
>         exit 1
>     fi
>     dd
>     for b in 210 421 843 1687 3375 6750 13500; do
>         echo $b > $m || exit 1
>         u="`cat $m`" || exit 1
>         v="`cat $a`" || exit 1
>         if [ "$u" != "$b" -o "$v" != "$d" ]; then
>             # one retry, cause we are in a race condition with the driver
>             echo $b > $m || exit 1
>             u="`cat $m`" || exit 1
>             v="`cat $a`" || exit 1
>             if [ "$u" != "$b" -o "$v" != "$d" ]; then
>                 echo -e "\nFailure: $m = $u (not $b)  $a = $v (not $d)"
>                 exit 1
>             fi
>         fi
>         d=$(($d/2))
>     done
>     echo -n " div OK"
>     # 2. test PWM vs DC mode
>     u="`cat ${map[$i]}_mode`" || exit 1
>     if [ "$u" != "1" ]; then
>         echo -e "\nLogic error: not in PWM mode"
>         exit 1
>     fi
>     read_fan_rpm $i
>     pwm_rpm=$l
>     set_pwm_dc ${map[$i]} 0 # DC mode
>     read_fan_rpm $i
>     d=$(($l-$pwm_rpm))
>     [ $d -lt 0 ] && d=$((-$d))
>     d=$(($d*10/$l))
>     if [ $d -gt 4 ]; then
>         echo -e "\nFailure: PWM $pwm_rpm RPM, DC $l RPM at 100%."
>         exit 1
>     fi
>     set_pwm_dc ${map[$i]} 1 # PWM mode
>     echo 128 > ${map[$i]} || exit 1
>     read_fan_rpm $i
>     pwm_rpm=$l
>     set_pwm_dc ${map[$i]} 0 # DC mode
>     read_fan_rpm $i
>     d=$(($l-$pwm_rpm))
>     [ $d -lt 0 ] && d=$((-$d))
>     d=$(($d*10/$l))
>     if [ $d -lt 4 ]; then
>         echo -e "\nFailure: PWM $pwm_rpm RPM, DC $l RPM at 50%."
>         exit 1
>     fi
>     echo -n ", PWM/DC OK"
>     if false; then
>         # disabled mode is not in the driver anymore...
>         # test disabled mode (RPM should increase)
>         echo 0 > ${map[$i]}_enable || exit 1
>         lorpm=$l
>         read_fan_rpm $i
>         d=$(($l-$lorpm))
>         d=$(($d*10/$lorpm))
>         if [ $d -lt 4 ]; then
>             echo -e "\nFailure: 50% $lorpm RPM, disabled (100%) $l RPM."
>             exit 1
>         fi
>         echo 1 > ${map[$i]}_enable || exit 1
>         echo -n ", enable OK"
>     fi
>
>     # 4. test SmartFan I mode (thermal cruise)
>     echo 2 > ${map[$i]}_enable || exit 1
>     #test_smartfan1 "$i" "${map[$i]}"
>     echo ", SF1 OK"
> done
>   
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070118/97973e58/attachment-0001.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (13 preceding siblings ...)
  2007-01-19  6:02 ` Daniel Ceregatti
@ 2007-01-19  6:31 ` David Hubbard
  2007-01-19  7:42 ` David Hubbard
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2007-01-19  6:31 UTC (permalink / raw)
  To: lm-sensors

Hi Daniel,

Thanks for the report! I hope we can resolve your setup. Let's go with
the newest version -- apply the patch or copy the file, your choice.
(For others who read this later, the patch is intended for 2.6.20-rc1.
2.6.19.2 is missing one patch updating a Rudolf's email address). If
the module loads, there should be files in sysfs. Assuming you have
that mounted at /sys, I think we can figure out what the problem is. I
don't think installing an -mm kernel is necessary, although that is
the official testing version at this point.

Have you updated the lm-sensors package to the latest version
released? 2.10.2 was just released, but the w83627ehf driver should
work fine with 2.10.1 on your w83627dhg. However, any earlier version
will not detect it correctly. In fact, can you upgrade to 2.10.2? It
would help tracking down bugs.

I'm signing on to irc.freenode.net #linux-sensors. Look for user
dch24. Either way, the report is appreciated!

David

On 1/18/07, Daniel Ceregatti <daniel at ceregatti.org> wrote:
>
>  Hi,
>
>  I have the EVGA 122-CK-NF68-AR motherboard, which supposedly has this chip.
> I've patched my 2.6.19.2 vanilla kernel with both the patch in this email (1
> hunk failed in Documentation, no big), and the full file, to no avail. The
> module loads, but sensors reports this for both:
>
>  w83627dhg-isa-0290
>  Adapter: ISA adapter
>
>  A while back I had spoken with Khali on IRC. He told me to change the
> w83627ehf.c (from vanilla 2.6.18 or so at the time) manually. I changed it
> thusly (This is against vanilla 2.6.19.2):
>
>  -- snip --
>
>  --- drivers/hwmon/w83627ehf.c.orig      2007-01-18
> 21:44:12.065293174 -0800
>  +++ drivers/hwmon/w83627ehf.c.manually_patched  2007-01-18
> 21:49:52.365378263 -0800
>  @@ -65,8 +65,11 @@
>   #define SIO_REG_ENABLE         0x30    /* Logical device enable */
>   #define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes)
> */
>
>  -#define SIO_W83627EHF_ID       0x8840
>  -#define SIO_ID_MASK            0xFFC0
>  +//#define SIO_W83627EHF_ID     0x8840
>  +//#define SIO_ID_MASK          0xFFC0
>  +
>  +#define SIO_W83627EHF_ID        0xA020
>  +#define SIO_ID_MASK            0xFFF0
>
>   static inline void
>   superio_outb(int reg, int val)
>
>  -- snip --
>
>  I load this module and run sensors:
>
>  w83627ehf-isa-0290
>  Adapter: ISA adapter
>  VCore:     +1.41 V  (min =  +0.00 V, max =  +1.74 V)
>  in1:      +11.14 V  (min =  +8.34 V, max = +11.83 V)
>  AVCC:      +3.20 V  (min =  +0.10 V, max =  +2.43 V) ALARM
>  3VCC:      +3.20 V  (min =  +1.15 V, max =  +3.57 V)
>  in4:       +1.48 V  (min =  +0.15 V, max =  +0.59 V) ALARM
>  in5:       +1.54 V  (min =  +0.72 V, max =  +1.71 V)
>  in6:       +5.86 V  (min =  +5.27 V, max =  +0.10 V) ALARM
>  VSB:       +3.20 V  (min =  +0.18 V, max =  +1.39 V) ALARM
>  VBAT:      +3.14 V  (min =  +2.64 V, max =  +3.84 V)
>  in9:       +0.10 V  (min =  +1.74 V, max =  +0.08 V) ALARM
>  Case Fan:    0 RPM  (min = 4560 RPM, div = 8) ALARM
>  CPU Fan:  2909 RPM  (min = 2636 RPM, div = 8)
>  Aux Fan:  1424 RPM  (min = 3879 RPM, div = 4) ALARM
>  fan4:        0 RPM  (min =  703 RPM, div = 32) ALARM
>  fan5:        0 RPM  (min = 3375 RPM, div = 8) ALARM
>  Sys Temp:    -65?C  (high =    +0?C, hyst =  +127?C)
>  CPU Temp:  +70.0?C  (high = +127.0?C, hyst =  +0.0?C)   ALARM
>  AUX Temp:  +57.0?C  (high = +127.0?C, hyst =  +0.0?C)
>
>  I'm guessing (hoping, actually) that most of that is wrong, but at the very
> least, I'm getting output.
>
>  Any thoughts as to how I should proceed? I suppose I could try the -mm
> tree, but I'm hesitant, as vanilla has always served me well.
>
>  Daniel Ceregatti
>
>  (Please CC me as I'm not on the list. Thanks)


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (14 preceding siblings ...)
  2007-01-19  6:31 ` David Hubbard
@ 2007-01-19  7:42 ` David Hubbard
  2007-01-19 22:04 ` David Holl
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2007-01-19  7:42 UTC (permalink / raw)
  To: lm-sensors

Update on the issue:

It looks like the new w83627ehf driver (with patch for official dhg
support) works fine. His sensors is reporting the following:

------------------
w83627dhg-isa-0290
Adapter: ISA adapter

coretemp-isa-0000
Adapter: ISA adapter
temp1:       +58?C  (high =   +85?C)

coretemp-isa-0001
Adapter: ISA adapter
temp1:       +57?C  (high =   +85?C)
------------------

(Note lack of any temperature info from w83627dhg)

But /sys/class/hwmon/hwmon0/device/temp1_input temp2_input temp3_input
seem OK. Well ... temp1_input = -65000. That's not right.

So sensors should be displaying something. That's as far as we got tonight.

David

> On 1/18/07, Daniel Ceregatti <daniel at ceregatti.org> wrote:
> >
> >  Hi,
> >
> >  I have the EVGA 122-CK-NF68-AR motherboard, which supposedly has this chip.
> > I've patched my 2.6.19.2 vanilla kernel with both the patch in this email (1
> > hunk failed in Documentation, no big), and the full file, to no avail. The
> > module loads, but sensors reports this for both:
> >
> >  w83627dhg-isa-0290
> >  Adapter: ISA adapter
> >
> >  A while back I had spoken with Khali on IRC. He told me to change the
> > w83627ehf.c (from vanilla 2.6.18 or so at the time) manually. I changed it
> > thusly (This is against vanilla 2.6.19.2):
> >
> >  -- snip --
> >
> >  --- drivers/hwmon/w83627ehf.c.orig      2007-01-18
> > 21:44:12.065293174 -0800
> >  +++ drivers/hwmon/w83627ehf.c.manually_patched  2007-01-18
> > 21:49:52.365378263 -0800
> >  @@ -65,8 +65,11 @@
> >   #define SIO_REG_ENABLE         0x30    /* Logical device enable */
> >   #define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes)
> > */
> >
> >  -#define SIO_W83627EHF_ID       0x8840
> >  -#define SIO_ID_MASK            0xFFC0
> >  +//#define SIO_W83627EHF_ID     0x8840
> >  +//#define SIO_ID_MASK          0xFFC0
> >  +
> >  +#define SIO_W83627EHF_ID        0xA020
> >  +#define SIO_ID_MASK            0xFFF0
> >
> >   static inline void
> >   superio_outb(int reg, int val)
> >
> >  -- snip --
> >
> >  I load this module and run sensors:
> >
> >  w83627ehf-isa-0290
> >  Adapter: ISA adapter
> >  VCore:     +1.41 V  (min =  +0.00 V, max =  +1.74 V)
> >  in1:      +11.14 V  (min =  +8.34 V, max = +11.83 V)
> >  AVCC:      +3.20 V  (min =  +0.10 V, max =  +2.43 V) ALARM
> >  3VCC:      +3.20 V  (min =  +1.15 V, max =  +3.57 V)
> >  in4:       +1.48 V  (min =  +0.15 V, max =  +0.59 V) ALARM
> >  in5:       +1.54 V  (min =  +0.72 V, max =  +1.71 V)
> >  in6:       +5.86 V  (min =  +5.27 V, max =  +0.10 V) ALARM
> >  VSB:       +3.20 V  (min =  +0.18 V, max =  +1.39 V) ALARM
> >  VBAT:      +3.14 V  (min =  +2.64 V, max =  +3.84 V)
> >  in9:       +0.10 V  (min =  +1.74 V, max =  +0.08 V) ALARM
> >  Case Fan:    0 RPM  (min = 4560 RPM, div = 8) ALARM
> >  CPU Fan:  2909 RPM  (min = 2636 RPM, div = 8)
> >  Aux Fan:  1424 RPM  (min = 3879 RPM, div = 4) ALARM
> >  fan4:        0 RPM  (min =  703 RPM, div = 32) ALARM
> >  fan5:        0 RPM  (min = 3375 RPM, div = 8) ALARM
> >  Sys Temp:    -65?C  (high =    +0?C, hyst =  +127?C)
> >  CPU Temp:  +70.0?C  (high = +127.0?C, hyst =  +0.0?C)   ALARM
> >  AUX Temp:  +57.0?C  (high = +127.0?C, hyst =  +0.0?C)
> >
> >  I'm guessing (hoping, actually) that most of that is wrong, but at the very
> > least, I'm getting output.
> >
> >  Any thoughts as to how I should proceed? I suppose I could try the -mm
> > tree, but I'm hesitant, as vanilla has always served me well.
> >
> >  Daniel Ceregatti
> >
> >  (Please CC me as I'm not on the list. Thanks)
>


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (15 preceding siblings ...)
  2007-01-19  7:42 ` David Hubbard
@ 2007-01-19 22:04 ` David Holl
  2007-01-19 22:11 ` Daniel Ceregatti
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Holl @ 2007-01-19 22:04 UTC (permalink / raw)
  To: lm-sensors

Hey Daniel, what version of lm-sensors do you have?  I found the "sensors"
command in lm-sensors 2.10.1 didn't know how to show the w83627dhg info, so
I submitted a fix which should be in 2.10.2.  :)

- David

On 1/19/07, David Hubbard <david.c.hubbard at gmail.com> wrote:
>
> Update on the issue:
>
> It looks like the new w83627ehf driver (with patch for official dhg
> support) works fine. His sensors is reporting the following:
>
> ------------------
> w83627dhg-isa-0290
> Adapter: ISA adapter
>
> coretemp-isa-0000
> Adapter: ISA adapter
> temp1:       +58?C  (high =   +85?C)
>
> coretemp-isa-0001
> Adapter: ISA adapter
> temp1:       +57?C  (high =   +85?C)
> ------------------
>
> (Note lack of any temperature info from w83627dhg)
>
> But /sys/class/hwmon/hwmon0/device/temp1_input temp2_input temp3_input
> seem OK. Well ... temp1_input = -65000. That's not right.
>
> So sensors should be displaying something. That's as far as we got
> tonight.
>
> David
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070119/4efe45c0/attachment.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (16 preceding siblings ...)
  2007-01-19 22:04 ` David Holl
@ 2007-01-19 22:11 ` Daniel Ceregatti
  2007-01-19 22:27 ` David Holl
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Ceregatti @ 2007-01-19 22:11 UTC (permalink / raw)
  To: lm-sensors

I have 2.10.1, which is the highest version my distro has. Should I
apply a patch? Grab the latest SVN?

Daniel

David Holl wrote:
> Hey Daniel, what version of lm-sensors do you have?  I found the
> "sensors" command in lm-sensors 2.10.1 didn't know how to show the
> w83627dhg info, so I submitted a fix which should be in 2.10.2.  :)
>
> - David
>
> On 1/19/07, *David Hubbard* <david.c.hubbard at gmail.com
> <mailto:david.c.hubbard at gmail.com>> wrote:
>
>     Update on the issue:
>
>     It looks like the new w83627ehf driver (with patch for official dhg
>     support) works fine. His sensors is reporting the following:
>
>     ------------------
>     w83627dhg-isa-0290
>     Adapter: ISA adapter
>
>     coretemp-isa-0000
>     Adapter: ISA adapter
>     temp1:       +58?C  (high =   +85?C)
>
>     coretemp-isa-0001
>     Adapter: ISA adapter
>     temp1:       +57?C  (high =   +85?C)
>     ------------------
>
>     (Note lack of any temperature info from w83627dhg)
>
>     But /sys/class/hwmon/hwmon0/device/temp1_input temp2_input temp3_input
>     seem OK. Well ... temp1_input = -65000. That's not right.
>
>     So sensors should be displaying something. That's as far as we got
>     tonight.
>
>     David
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070119/9695b3da/attachment.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (17 preceding siblings ...)
  2007-01-19 22:11 ` Daniel Ceregatti
@ 2007-01-19 22:27 ` David Holl
  2007-01-20  5:37 ` Daniel Ceregatti
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Holl @ 2007-01-19 22:27 UTC (permalink / raw)
  To: lm-sensors

Yeah, my distro only has 2.10.1 as well.  (Gentoo -- hopefully they'll put
it up under ~x86 soon... /me wonders how to become the Gentoo package
maintainer for lm_sensors...)

Try 2.10.2 from http://www.lm-sensors.org/wiki/Download


On 1/19/07, Daniel Ceregatti <daniel at ceregatti.org> wrote:
>
>  I have 2.10.1, which is the highest version my distro has. Should I apply
> a patch? Grab the latest SVN?
>
> Daniel
>
> David Holl wrote:
>
> Hey Daniel, what version of lm-sensors do you have?  I found the "sensors"
> command in lm-sensors 2.10.1 didn't know how to show the w83627dhg info,
> so I submitted a fix which should be in 2.10.2.  :)
>
> - David
>
> On 1/19/07, David Hubbard <david.c.hubbard at gmail.com> wrote:
> >
> > Update on the issue:
> >
> > It looks like the new w83627ehf driver (with patch for official dhg
> > support) works fine. His sensors is reporting the following:
> >
> > ------------------
> > w83627dhg-isa-0290
> > Adapter: ISA adapter
> >
> > coretemp-isa-0000
> > Adapter: ISA adapter
> > temp1:       +58?C  (high =   +85?C)
> >
> > coretemp-isa-0001
> > Adapter: ISA adapter
> > temp1:       +57?C  (high =   +85?C)
> > ------------------
> >
> > (Note lack of any temperature info from w83627dhg)
> >
> > But /sys/class/hwmon/hwmon0/device/temp1_input temp2_input temp3_input
> > seem OK. Well ... temp1_input = -65000. That's not right.
> >
> > So sensors should be displaying something. That's as far as we got
> > tonight.
> >
> > David
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070119/4ddff372/attachment.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (18 preceding siblings ...)
  2007-01-19 22:27 ` David Holl
@ 2007-01-20  5:37 ` Daniel Ceregatti
  2007-01-20 19:01 ` David Hubbard
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Ceregatti @ 2007-01-20  5:37 UTC (permalink / raw)
  To: lm-sensors

Hi all,

Seems I also had libsensors from a manually compiled lm_sensors in
/usr/local/lib. I've managed to remove all traces of this, installed
2.10.2 via portage by making my own ebuild (overlay, in case you want
it, is here: http://sh.nu/download/ebuilds) and:

w83627dhg-isa-0290
Adapter: ISA adapter
VCore:     +1.18 V  (min =  +0.00 V, max =  +1.74 V)
in1:      +11.14 V  (min =  +8.40 V, max = +12.04 V)
AVCC:      +3.20 V  (min =  +2.14 V, max =  +0.43 V) ALARM
3VCC:      +3.20 V  (min =  +1.15 V, max =  +2.13 V) ALARM
in4:       +1.18 V  (min =  +0.18 V, max =  +0.11 V) ALARM
in5:       +1.54 V  (min =  +0.69 V, max =  +1.71 V)
in6:       +5.86 V  (min =  +5.27 V, max =  +0.00 V) ALARM
VSB:       +3.20 V  (min =  +0.18 V, max =  +1.90 V) ALARM
VBAT:      +3.14 V  (min =  +2.13 V, max =  +1.34 V) ALARM
Case Fan:    0 RPM  (min = 4500 RPM, div = 4) ALARM
CPU Fan:  2083 RPM  (min = 84375 RPM, div = 8) ALARM
Aux Fan:     0 RPM  (min = 4066 RPM, div = 4) ALARM
fan4:        0 RPM  (min =  703 RPM, div = 16) ALARM
fan5:        0 RPM  (min = 6958 RPM, div = 2) ALARM
Sys Temp:    -65?C  (high =    +0?C, hyst =  +127?C) 
CPU Temp:  +53.5?C  (high = +127.0?C, hyst =  +0.0?C)   ALARM
AUX Temp:  +54.5?C  (high = +127.0?C, hyst =  +0.0?C) 

coretemp-isa-0000
Adapter: ISA adapter
temp1:       +56?C  (high =   +85?C)                    

coretemp-isa-0001
Adapter: ISA adapter
temp1:       +57?C  (high =   +85?C)                    

Here is a screen shot of my bios:

http://sh.nu/download/images/bios.jpg

I guess the next step is to configure it? Seems I'm missing a few things
there.

Daniel

David Holl wrote:
> Yeah, my distro only has 2.10.1 as well.  (Gentoo -- hopefully they'll
> put it up under ~x86 soon... /me wonders how to become the Gentoo
> package maintainer for lm_sensors...)
>
> Try 2.10.2 from http://www.lm-sensors.org/wiki/Download
>
>
> On 1/19/07, *Daniel Ceregatti* <daniel at ceregatti.org
> <mailto:daniel at ceregatti.org>> wrote:
>
>     I have 2.10.1, which is the highest version my distro has. Should
>     I apply a patch? Grab the latest SVN?
>
>     Daniel
>
>
>     David Holl wrote:
>>     Hey Daniel, what version of lm-sensors do you have?  I found the
>>     "sensors" command in lm-sensors 2.10.1 didn't know how to show
>>     the w83627dhg info, so I submitted a fix which should be in
>>     2.10.2.  :)
>>
>>     - David
>>
>>     On 1/19/07, *David Hubbard* <david.c.hubbard at gmail.com
>>     <mailto:david.c.hubbard at gmail.com>> wrote:
>>
>>         Update on the issue:
>>
>>         It looks like the new w83627ehf driver (with patch for
>>         official dhg
>>         support) works fine. His sensors is reporting the following:
>>
>>         ------------------
>>         w83627dhg-isa-0290
>>         Adapter: ISA adapter
>>
>>         coretemp-isa-0000
>>         Adapter: ISA adapter
>>         temp1:       +58?C  (high =   +85?C)
>>
>>         coretemp-isa-0001
>>         Adapter: ISA adapter
>>         temp1:       +57?C  (high =   +85?C)
>>         ------------------
>>
>>         (Note lack of any temperature info from w83627dhg)
>>
>>         But /sys/class/hwmon/hwmon0/device/temp1_input temp2_input
>>         temp3_input
>>         seem OK. Well ... temp1_input = -65000. That's not right.
>>
>>         So sensors should be displaying something. That's as far as
>>         we got tonight.
>>
>>         David
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070119/e58dd0da/attachment-0001.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (19 preceding siblings ...)
  2007-01-20  5:37 ` Daniel Ceregatti
@ 2007-01-20 19:01 ` David Hubbard
  2007-01-22 15:51 ` David Holl
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2007-01-20 19:01 UTC (permalink / raw)
  To: lm-sensors

Hi all,

>  David Holl wrote:
> Yeah, my distro only has 2.10.1 as well.  (Gentoo -- hopefully they'll put
> it up under ~x86 soon... /me wonders how to become the Gentoo package
> maintainer for lm_sensors...)
>
>  Try 2.10.2 from http://www.lm-sensors.org/wiki/Download

I'm a gentoo fan also! If you become package maintainer, please let me
know... I'd love to see lm_sensors get more attention in gentoo.

On 1/19/07, Daniel Ceregatti <daniel at ceregatti.org> wrote:
>
>  Hi all,
>
>  Seems I also had libsensors from a manually compiled lm_sensors in
> /usr/local/lib. I've managed to remove all traces of this, installed 2.10.2
> via portage by making my own ebuild (overlay, in case you want it, is here:
> http://sh.nu/download/ebuilds) and:
>
>  w83627dhg-isa-0290
>  Adapter: ISA adapter
>  VCore:     +1.18 V  (min =  +0.00 V, max =  +1.74 V)
>  in1:      +11.14 V  (min =  +8.40 V, max = +12.04 V)
>  AVCC:      +3.20 V  (min =  +2.14 V, max =  +0.43 V) ALARM
>  3VCC:      +3.20 V  (min =  +1.15 V, max =  +2.13 V) ALARM
>  in4:       +1.18 V  (min =  +0.18 V, max =  +0.11 V) ALARM
>  in5:       +1.54 V  (min =  +0.69 V, max =  +1.71 V)
>  in6:       +5.86 V  (min =  +5.27 V, max =  +0.00 V) ALARM
>  VSB:       +3.20 V  (min =  +0.18 V, max =  +1.90 V) ALARM
>  VBAT:      +3.14 V  (min =  +2.13 V, max =  +1.34 V) ALARM
>  Case Fan:    0 RPM  (min = 4500 RPM, div = 4) ALARM
>  CPU Fan:  2083 RPM  (min = 84375 RPM, div = 8) ALARM
>  Aux Fan:     0 RPM  (min = 4066 RPM, div = 4) ALARM
>  fan4:        0 RPM  (min =  703 RPM, div = 16) ALARM
>  fan5:        0 RPM  (min = 6958 RPM, div = 2) ALARM
>  Sys Temp:    -65?C  (high =    +0?C, hyst =  +127?C)
>  CPU Temp:  +53.5?C  (high = +127.0?C, hyst =  +0.0?C)   ALARM
>  AUX Temp:  +54.5?C  (high = +127.0?C, hyst =  +0.0?C)
>
>  coretemp-isa-0000
>  Adapter: ISA adapter
>  temp1:       +56?C  (high =   +85?C)
>
>  coretemp-isa-0001
>  Adapter: ISA adapter
>  temp1:       +57?C  (high =   +85?C)
>
>  Here is a screen shot of my bios:
>
>  http://sh.nu/download/images/bios.jpg
>
>  I guess the next step is to configure it? Seems I'm missing a few things
> there.
>
>  Daniel

Daniel, I'm kicking myself right now. That's the easy solution, eh?
Now, when you modify sensors.conf, you'll be putting in
motherboard-specific information. We can push that upstream to the
lm_sensors project. I have an Asus P5B, and we should be able to find
just what's different between that and your EVGA 122-CK-NF68-AR.

I haven't gone through and fine-tuned my sensors.conf yet. I guess I
should do that this weekend.

David


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (20 preceding siblings ...)
  2007-01-20 19:01 ` David Hubbard
@ 2007-01-22 15:51 ` David Holl
  2007-01-31  1:36 ` Joe Harvell
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: David Holl @ 2007-01-22 15:51 UTC (permalink / raw)
  To: lm-sensors

woo hoo -- 2.10.2 was included in Gentoo (~x86) on Jan 20!
http://packages.gentoo.org/search/?sstring=lm_sensors

On 1/20/07, David Hubbard <david.c.hubbard at gmail.com> wrote:
>
> Hi all,
>
> >  David Holl wrote:
> > Yeah, my distro only has 2.10.1 as well.  (Gentoo -- hopefully they'll
> put
> > it up under ~x86 soon... /me wonders how to become the Gentoo package
> > maintainer for lm_sensors...)
> >
> >  Try 2.10.2 from http://www.lm-sensors.org/wiki/Download
>
> I'm a gentoo fan also! If you become package maintainer, please let me
> know... I'd love to see lm_sensors get more attention in gentoo.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070122/7582d21d/attachment.html 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (21 preceding siblings ...)
  2007-01-22 15:51 ` David Holl
@ 2007-01-31  1:36 ` Joe Harvell
  2007-02-04 16:49 ` Rudolf Marek
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Joe Harvell @ 2007-01-31  1:36 UTC (permalink / raw)
  To: lm-sensors

I'm replying to a recent post I found in the archives:  
http://lists.lm-sensors.org/pipermail/lm-sensors/2007-January/018563.html

> in0 VCore
> in1 12V
> in2 not monitored
> in3 3.3V
> in4 not monitored
> in5 5V
>
>
> No formulas are used. Just raw value * 8mV so no need for compute lines in
> sensors.conf
>
> TEMP1 should be MBTEMP, which is direct value, temp2 is CPU temp, temp3 is not
> used in their implementation - but conversion formula is same as for the temp2.
> So it is most likely floating or not connected.
>
> As for fans:
>
> fan1 is cpu
> fan2 is chassis1
> fan3 is chassis2
> fan4 is power fan
> fan5 is not monitored
>
> All in all it seems that gkrellm is wrong. Please use the version indicated
> above, and provide the output of sensors command so we can check that the
> readings match the reality (and we trust our tools ;)
>   


I have an Asus P5B Deluxe (not the Wi-Fi edition), and the readings I'm 
getting don't match the conclusions you made from the DSDT.  I flashed 
to the latest BIOS (1004), and my DSDT looks basically the same as what 
you show.  But here is what I'm getting.  I commented out all of the 
"label" and "compute" lines so you can see the raw data.  Anyway, you 
say there should be no need for computes:

cujo kernel # sensors
w83627dhg-isa-0290
Adapter: ISA adapter
in0:       +1.12 V  (min =  +0.00 V, max =  +1.74 V)
in1:       +1.84 V  (min =  +1.64 V, max =  +2.00 V)
in2:       +3.33 V  (min =  +0.59 V, max =  +0.53 V) ALARM
in3:       +3.33 V  (min =  +2.43 V, max =  +0.35 V) ALARM
in4:       +1.58 V  (min =  +1.28 V, max =  +0.00 V) ALARM
in5:       +1.56 V  (min =  +1.62 V, max =  +0.17 V) ALARM
in6:       +1.58 V  (min =  +1.49 V, max =  +1.64 V)
in7:       +3.33 V  (min =  +0.29 V, max =  +3.36 V)
in8:       +2.11 V  (min =  +0.29 V, max =  +1.66 V) ALARM
fan1:     1102 RPM  (min = 8437 RPM, div = 8) ALARM
fan2:     2537 RPM  (min = 4440 RPM, div = 4) ALARM
fan3:        0 RPM  (min = 5273 RPM, div = 128) ALARM
fan4:        0 RPM  (min = 10546 RPM, div = 128) ALARM
fan5:        0 RPM  (min = 168750 RPM, div = 8) ALARM
temp1:       +28 C  (high =   +26 C, hyst =   +34 C)
temp2:     +34.0 C  (high = +80.0 C, hyst = +75.0 C)
temp3:    +119.0 C  (high = +91.0 C, hyst = +89.0 C)   ALARM

Here are my comments:

in1 (you state it should be 12V) is incorrect.  However, if I use the 
compute line from lm-sensors-2.10.2 it is in the 12V range.
in5 (you state it should be 5V) is incorrect.  However, 
lm-sensors-2.10.2 shows the 5V is in6 with a compute line, which matches 
what I am getting.

temp1: (you state it should be MB) this is consistent with my BIOS monitor
temp2: (you state it should be CPU)  My temp2 went up from 34.0 to 44 
under load and then came back down to 35.5 almost immediately.
            So this does look like temp2 is linked to my CPU.  But you 
mention a conversion formula for temp2 and temp3.  What formula
            did you deduce from DSDT?  lm-sensors-2.10.2 has no compute 
line.  So I don't know if the temp is accurate.
temp3: (you state unused but with the same conversion formula as 
temp2):  Under load, my temp3 *drops* from 119 to 103.5 under load and 
then goes back up afterwards ?????

fan1: (you state it should be the CPU_FAN) The RPM in the 1100 range 
matches what I'm seeing in the BIOS for CHA_FAN1 and CHA_FAN2 (both are 
documented to run at 1800 RPM)
fan2: (you state it should be CHA_FAN1) The RPM of 2537 is consistent 
with what I'm seeing in the BIOS for CPU_FAN (a Zalman CNPS9500 LED)
fan3: (you state it should be CHA_FAN2) My fan3 shows 0, but I have 
verified both fans are spinning and connected to CHA_FAN1 and CHA_FAN2
fan4: (you state this is PWR_FAN):  The P5B manual states that this 
connector is not supported by their QFan2 software monitoring.  I doubt 
it would be monitorable.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (22 preceding siblings ...)
  2007-01-31  1:36 ` Joe Harvell
@ 2007-02-04 16:49 ` Rudolf Marek
  2007-02-05 18:43 ` Joe Harvell
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Rudolf Marek @ 2007-02-04 16:49 UTC (permalink / raw)
  To: lm-sensors

Hello again,

> in1 (you state it should be 12V) is incorrect.  However, if I use the 
> compute line from lm-sensors-2.10.2 it is in the 12V range.
> in5 (you state it should be 5V) is incorrect.  However, 
> lm-sensors-2.10.2 shows the 5V is in6 with a compute line, which matches 
> what I am getting.

Ok then. They must do the convention elsewhere than on DSDT (oh those ... :)

> temp1: (you state it should be MB) this is consistent with my BIOS monitor
> temp2: (you state it should be CPU)  My temp2 went up from 34.0 to 44 
> under load and then came back down to 35.5 almost immediately.
>             So this does look like temp2 is linked to my CPU.  But you 
> mention a conversion formula for temp2 and temp3.  What formula
>             did you deduce from DSDT? 

None it seems, just generic formula for for 1 decimal point.

> temp2):  Under load, my temp3 *drops* from 119 to 103.5 under load and 
> then goes back up afterwards ?????

Hmm my MB has this at 127C which means unconnected. Please can you send a
dsdt.bin file? (cat /proc/acpi/dsdt > /tmp/dsdt.bin)

> fan1: (you state it should be the CPU_FAN) The RPM in the 1100 range 
> matches what I'm seeing in the BIOS for CHA_FAN1 and CHA_FAN2 (both are 
> documented to run at 1800 RPM)

ok

> fan2: (you state it should be CHA_FAN1) The RPM of 2537 is consistent 
> with what I'm seeing in the BIOS for CPU_FAN (a Zalman CNPS9500 LED)
> fan3: (you state it should be CHA_FAN2) My fan3 shows 0, but I have 
> verified both fans are spinning and connected to CHA_FAN1 and CHA_FAN2
> fan4: (you state this is PWR_FAN):  The P5B manual states that this 
> connector is not supported by their QFan2 software monitoring.  I doubt 
> it would be monitorable.

Hmm how many/what fans can you see in the BIOS?

here is my:

axon:~# sensors
w83627dhg-isa-0290
Adapter: ISA adapter
VCore:     +1.14 V  (min =  +0.00 V, max =  +1.74 V)
in1:      +12.30 V  (min =  +0.26 V, max = +10.88 V) ALARM
AVCC:      +3.25 V  (min =  +1.74 V, max =  +2.24 V) ALARM
3VCC:      +3.25 V  (min =  +3.44 V, max =  +3.17 V) ALARM
in4:       +1.11 V  (min =  +1.17 V, max =  +0.26 V) ALARM
in5:       +1.59 V  (min =  +0.06 V, max =  +1.54 V) ALARM
in6:       +3.53 V  (min =  +0.51 V, max =  +1.00 V) ALARM
VSB:       +3.25 V  (min =  +1.74 V, max =  +3.66 V)
VBAT:      +3.25 V  (min =  +0.05 V, max =  +0.64 V) ALARM
Case Fan:    0 RPM  (min = 21093 RPM, div = 64) ALARM
CPU Fan:  1687 RPM  (min = 1662 RPM, div = 4)
Aux Fan:     0 RPM  (min = 10546 RPM, div = 64) ALARM
fan4:        0 RPM  (min = 10546 RPM, div = 32) ALARM
fan5:     3835 RPM  (min = 7670 RPM, div = 2) ALARM
Sys Temp:    +33?C  (high =   -56?C, hyst =   +39?C)
CPU Temp:  +39.5?C  (high = +80.0?C, hyst = +75.0?C)
AUX Temp: +127.0?C  (high = +80.0?C, hyst = +75.0?C)   ALARM

coretemp-isa-0000
Adapter: ISA adapter
temp1:       +30?C  (high =   +85?C)

coretemp-isa-0001
Adapter: ISA adapter
temp1:       +32?C  (high =   +85?C)

Rudolf


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (23 preceding siblings ...)
  2007-02-04 16:49 ` Rudolf Marek
@ 2007-02-05 18:43 ` Joe Harvell
  2007-02-06  3:54 ` Joe Harvell
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Joe Harvell @ 2007-02-05 18:43 UTC (permalink / raw)
  To: lm-sensors

Rudolf Marek wrote:
>
>> temp2):  Under load, my temp3 *drops* from 119 to 103.5 under load and 
>> then goes back up afterwards ?????
>>     
>
> Hmm my MB has this at 127C which means unconnected. Please can you send a
> dsdt.bin file? (cat /proc/acpi/dsdt > /tmp/dsdt.bin)
>   
This machine is powered down right now.  I'll send this shortly.
>   
>> fan1: (you state it should be the CPU_FAN) The RPM in the 1100 range 
>> matches what I'm seeing in the BIOS for CHA_FAN1 and CHA_FAN2 (both are 
>> documented to run at 1800 RPM)
>>     
>
> ok
>
>   
>> fan2: (you state it should be CHA_FAN1) The RPM of 2537 is consistent 
>> with what I'm seeing in the BIOS for CPU_FAN (a Zalman CNPS9500 LED)
>> fan3: (you state it should be CHA_FAN2) My fan3 shows 0, but I have 
>> verified both fans are spinning and connected to CHA_FAN1 and CHA_FAN2
>> fan4: (you state this is PWR_FAN):  The P5B manual states that this 
>> connector is not supported by their QFan2 software monitoring.  I doubt 
>> it would be monitorable.
>>     
>
> Hmm how many/what fans can you see in the BIOS?
>   
I'll get that shortly also.  Just wanted to let you know that I see some 
strange behavior of lm_sensors with respect to the fan5.  This sensor 
usually reports zero.  But after it has been up for a while it starts 
reporting something in the 1100 to 1200 range.  This is consistent with 
my case fan speed.  So it seems fan5 is either CHA_FAN1 or CHA_FAN2.  
Actually, I can't confirm it's the fan5 sensor that's doing this.  That 
is what I remember from a couple of days ago.  But I updated my 
sensors.conf file to put a label on the sensor that was doing this, so 
once I power up my machine again, I'll verify this as well.
> here is my:
>
> axon:~# sensors
> w83627dhg-isa-0290
> Adapter: ISA adapter
> VCore:     +1.14 V  (min =  +0.00 V, max =  +1.74 V)
> in1:      +12.30 V  (min =  +0.26 V, max = +10.88 V) ALARM
> AVCC:      +3.25 V  (min =  +1.74 V, max =  +2.24 V) ALARM
> 3VCC:      +3.25 V  (min =  +3.44 V, max =  +3.17 V) ALARM
> in4:       +1.11 V  (min =  +1.17 V, max =  +0.26 V) ALARM
> in5:       +1.59 V  (min =  +0.06 V, max =  +1.54 V) ALARM
> in6:       +3.53 V  (min =  +0.51 V, max =  +1.00 V) ALARM
> VSB:       +3.25 V  (min =  +1.74 V, max =  +3.66 V)
> VBAT:      +3.25 V  (min =  +0.05 V, max =  +0.64 V) ALARM
> Case Fan:    0 RPM  (min = 21093 RPM, div = 64) ALARM
> CPU Fan:  1687 RPM  (min = 1662 RPM, div = 4)
> Aux Fan:     0 RPM  (min = 10546 RPM, div = 64) ALARM
> fan4:        0 RPM  (min = 10546 RPM, div = 32) ALARM
> fan5:     3835 RPM  (min = 7670 RPM, div = 2) ALARM
> Sys Temp:    +33?C  (high =   -56?C, hyst =   +39?C)
> CPU Temp:  +39.5?C  (high = +80.0?C, hyst = +75.0?C)
> AUX Temp: +127.0?C  (high = +80.0?C, hyst = +75.0?C)   ALARM
>
> coretemp-isa-0000
> Adapter: ISA adapter
> temp1:       +30?C  (high =   +85?C)
>
> coretemp-isa-0001
> Adapter: ISA adapter
> temp1:       +32?C  (high =   +85?C)
>
> Rudolf
>   
I forget, is your board a P5B Deluxe WiFi?


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (24 preceding siblings ...)
  2007-02-05 18:43 ` Joe Harvell
@ 2007-02-06  3:54 ` Joe Harvell
  2007-02-06 17:11 ` Joe Harvell
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Joe Harvell @ 2007-02-06  3:54 UTC (permalink / raw)
  To: lm-sensors

Joe Harvell wrote:
> Rudolf Marek wrote:
>   
>>> temp2):  Under load, my temp3 *drops* from 119 to 103.5 under load and 
>>> then goes back up afterwards ?????
>>>     
>>>       
>> Hmm my MB has this at 127C which means unconnected. Please can you send a
>> dsdt.bin file? (cat /proc/acpi/dsdt > /tmp/dsdt.bin)
>>   
>>     
> This machine is powered down right now.  I'll send this shortly.
>   
Sent.
>
> I'll get that shortly also.  Just wanted to let you know that I see some 
> strange behavior of lm_sensors with respect to the fan5.  This sensor 
> usually reports zero.  But after it has been up for a while it starts 
> reporting something in the 1100 to 1200 range.  This is consistent with 
> my case fan speed.  So it seems fan5 is either CHA_FAN1 or CHA_FAN2.  
> Actually, I can't confirm it's the fan5 sensor that's doing this.  That 
> is what I remember from a couple of days ago.  But I updated my 
> sensors.conf file to put a label on the sensor that was doing this, so 
> once I power up my machine again, I'll verify this as well.
>   
Confirmed, fan5 is the sensors showing false zero readings most of the 
time and valid readings for CHA_FAN1 or CHA_FAN2 sometimes.

I need to hook up a console again to get the BIOS fans.
>> here is my:
>>
>> axon:~# sensors
>> w83627dhg-isa-0290
>> Adapter: ISA adapter
>> VCore:     +1.14 V  (min =  +0.00 V, max =  +1.74 V)
>> in1:      +12.30 V  (min =  +0.26 V, max = +10.88 V) ALARM
>> AVCC:      +3.25 V  (min =  +1.74 V, max =  +2.24 V) ALARM
>> 3VCC:      +3.25 V  (min =  +3.44 V, max =  +3.17 V) ALARM
>> in4:       +1.11 V  (min =  +1.17 V, max =  +0.26 V) ALARM
>> in5:       +1.59 V  (min =  +0.06 V, max =  +1.54 V) ALARM
>> in6:       +3.53 V  (min =  +0.51 V, max =  +1.00 V) ALARM
>> VSB:       +3.25 V  (min =  +1.74 V, max =  +3.66 V)
>> VBAT:      +3.25 V  (min =  +0.05 V, max =  +0.64 V) ALARM
>> Case Fan:    0 RPM  (min = 21093 RPM, div = 64) ALARM
>> CPU Fan:  1687 RPM  (min = 1662 RPM, div = 4)
>> Aux Fan:     0 RPM  (min = 10546 RPM, div = 64) ALARM
>> fan4:        0 RPM  (min = 10546 RPM, div = 32) ALARM
>> fan5:     3835 RPM  (min = 7670 RPM, div = 2) ALARM
>> Sys Temp:    +33?C  (high =   -56?C, hyst =   +39?C)
>> CPU Temp:  +39.5?C  (high = +80.0?C, hyst = +75.0?C)
>> AUX Temp: +127.0?C  (high = +80.0?C, hyst = +75.0?C)   ALARM
>>
>> coretemp-isa-0000
>> Adapter: ISA adapter
>> temp1:       +30?C  (high =   +85?C)
>>
>> coretemp-isa-0001
>> Adapter: ISA adapter
>> temp1:       +32?C  (high =   +85?C)
>>
>> Rudolf
>>   
>>     
> I forget, is your board a P5B Deluxe WiFi?
>
> _______________________________________________
> lm-sensors mailing list
> lm-sensors at lm-sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/lm-sensors



^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (25 preceding siblings ...)
  2007-02-06  3:54 ` Joe Harvell
@ 2007-02-06 17:11 ` Joe Harvell
  2007-02-07 18:41 ` David Hubbard
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Joe Harvell @ 2007-02-06 17:11 UTC (permalink / raw)
  To: lm-sensors

Rudolf Marek wrote:

> 
>> fan1: (you state it should be the CPU_FAN) The RPM in the 1100 range 
>> matches what I'm seeing in the BIOS for CHA_FAN1 and CHA_FAN2 (both are 
>> documented to run at 1800 RPM)
> 
> ok
> 
>> fan2: (you state it should be CHA_FAN1) The RPM of 2537 is consistent 
>> with what I'm seeing in the BIOS for CPU_FAN (a Zalman CNPS9500 LED)
>> fan3: (you state it should be CHA_FAN2) My fan3 shows 0, but I have 
>> verified both fans are spinning and connected to CHA_FAN1 and CHA_FAN2
>> fan4: (you state this is PWR_FAN):  The P5B manual states that this 
>> connector is not supported by their QFan2 software monitoring.  I doubt 
>> it would be monitorable.
> 
> Hmm how many/what fans can you see in the BIOS?

I figured out why I got zero readings for fan5.  Since my case fans are 
lower speed, it seems the divisor was to low.  I remedied this by 
settting fan1_min and fan5_min to 700.  This also has the side effect of 
setting the divisor to 16.  Now I get readings in the 1100 RPM range for 
both fan1 and fan5.

Here is what I see in the BIOS Hardware Monitor after being on for about 
5 minutes:

CPU Temperature          35.5 C
MB Temperature           21 C
CPU Fan Speed            2556
CPU QFan Control         Enabled
CPU Fan Profile          Optimal
Chassis Fan1 Speed       1095
Chassis Fan2 Speed       1163
Chassis Fan3 Speed       N/A
Chassis QFan Control     Enabled
Chassis Fan Profile      Optimal
Power Fan Speed          N/A

VCore Voltage             1.176
3.3V Voltage              3.328
5V Voltage                4.992
12V Voltage              12.144

Here is my Idle sensors output

jharvell at cujo ~ $ sensors
w83627dhg-isa-0290
Adapter: ISA adapter
VCore:     +1.12 V  (min =  +0.00 V, max =  +1.74 V)
12 V:     +12.14 V  (min = +10.82 V, max = +13.20 V)
3.3V:      +3.33 V  (min =  +3.14 V, max =  +3.47 V)
5V:        +5.04 V  (min =  +4.76 V, max =  +5.25 V)
VSB:       +3.33 V  (min =  +3.14 V, max =  +3.47 V)
CHA_FAN1: 1125 RPM  (min =  703 RPM, div = 16)
CPU_FAN:  2518 RPM  (min = 1704 RPM, div = 8)
CHA_FAN2: 1222 RPM  (min =  703 RPM, div = 16)
Sys Temp:    +28 C  (high =   +40 C, hyst =   +35 C)
CPU Temp:  +34.5 C  (high = +55.0 C, hyst = +45.0 C)

Actually, I recently finished a compile, so it may still be cooling 
slightly.  Also, I am using CPU Frequency scaling.  So the CPU is at 
1.60 GHz instead of the normal 2.39 GHz.  I don't know what the 
operating CPU frequency is in the BIOS.

jharvell at cujo ~ $ cpufreq-info
cpufrequtils 002: cpufreq-info (C) Dominik Brodowski 2004-2006
Report errors and bugs to linux at brodo.de, please.
analyzing CPU 0:
   driver: centrino
   CPUs which need to switch frequency at the same time: 0
   hardware limits: 1.60 GHz - 2.39 GHz
   available frequency steps: 2.39 GHz, 1.60 GHz
   available cpufreq governors: ondemand, performance
   current policy: frequency should be within 1.60 GHz and 2.39 GHz.
                   The governor "ondemand" may decide which speed to use
                   within this range.
   current CPU frequency is 1.60 GHz.
analyzing CPU 1:
   driver: centrino
   CPUs which need to switch frequency at the same time: 1
   hardware limits: 1.60 GHz - 2.39 GHz
   available frequency steps: 2.39 GHz, 1.60 GHz
   available cpufreq governors: ondemand, performance
   current policy: frequency should be within 1.60 GHz and 2.39 GHz.
                   The governor "ondemand" may decide which speed to use
                   within this range.
   current CPU frequency is 1.60 GHz.


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (26 preceding siblings ...)
  2007-02-06 17:11 ` Joe Harvell
@ 2007-02-07 18:41 ` David Hubbard
  2007-02-08 19:44 ` Rudolf Marek
  2007-02-12  9:04 ` Brett King
  29 siblings, 0 replies; 31+ messages in thread
From: David Hubbard @ 2007-02-07 18:41 UTC (permalink / raw)
  To: lm-sensors

Hi Brett,

I am sending you the patch and the full driver.

It's actually pretty easy to use the patch. You just cd into your
kernel directory, say /usr/src/linux-2.6.18

# cd /usr/src/linux-2.6.18
linux-2.6.18 #

Then you run 'patch' like this:

linux-2.6.18 # patch -p1 -i
/home/yourdirectory/somepath/w83627ehf_add_w83627dhg_support.patch.txt

It will tell you it is patching drivers/hwmon/w83627ehf.c and then you
have the driver installed and ready to go.

linux-2.6.18 # make
linux-2.6.18 # make modules_install

Any other kernel compile steps would go here. If you are lucky, you
won't even need to reboot. The new module will overwrite the old
w83627ehf.ko and you can just load the new module like this:

linux-2.6.18 # modprobe w83627ehf


Finally, the original patch is at this link with all the same information.
http://lists.lm-sensors.org/pipermail/lm-sensors/2006-December/018519.html

Hope that helps,
David
-------------- next part --------------
This patch adds support for the w83627dhg chip.
Signed-off-by: David Hubbard <david.c.hubbard at gmail.com>

--- old/drivers/hwmon/w83627ehf.c	2006-12-21 14:04:19.000000000 -0800
+++ new/drivers/hwmon/w83627ehf.c	2006-12-24 21:53:54.000000000 -0800
@@ -32,8 +32,10 @@
 
     Supports the following chips:
 
-    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
-    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
+    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
+    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
+                                               0x8860 0xa1
+    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
 */
 
 #include <linux/module.h>
@@ -55,8 +57,18 @@
  * Super-I/O constants and functions
  */
 
+/*
+ * The three following globals are initialized in w83627ehf_find(), before
+ * the i2c-isa device is created. Otherwise, they could be stored in
+ * w83627ehf_data. This is ugly, but necessary. and when the driver is next
+ * updated to become a platform driver, the globals will disappear.
+ */
 static int REG;		/* The register to read/write */
 static int VAL;		/* The value to read/write */
+/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
+ * value is also used in w83627ehf_detect() to export a device name in sysfs
+ * (e.g. w83627ehf or w83627dhg) */
+static int w83627ehf_num_in;
 
 #define W83627EHF_LD_HWM	0x0b
 
@@ -65,8 +77,10 @@
 #define SIO_REG_ENABLE		0x30	/* Logical device enable */
 #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
 
-#define SIO_W83627EHF_ID	0x8840
-#define SIO_ID_MASK		0xFFC0
+#define SIO_W83627EHF_ID	0x8850
+#define SIO_W83627EHG_ID	0x8860
+#define SIO_W83627DHG_ID	0xa020
+#define SIO_ID_MASK		0xFFF0
 
 static inline void
 superio_outb(int reg, int val)
@@ -115,8 +129,12 @@
 
 #define W83627EHF_REG_BANK		0x4E
 #define W83627EHF_REG_CONFIG		0x40
-#define W83627EHF_REG_CHIP_ID		0x49
-#define W83627EHF_REG_MAN_ID		0x4F
+
+/* Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58 */
 
 static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
 static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@@ -429,7 +447,7 @@
 		}
 
 		/* Measured voltages and limits */
-		for (i = 0; i < 10; i++) {
+		for (i = 0; i < w83627ehf_num_in; i++) {
 			data->in[i] = w83627ehf_read_value(client,
 				      W83627EHF_REG_IN(i));
 			data->in_min[i] = w83627ehf_read_value(client,
@@ -1121,7 +1139,7 @@
 		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
 	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
 		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < w83627ehf_num_in; i++) {
 		device_remove_file(dev, &sda_in_input[i].dev_attr);
 		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
 		device_remove_file(dev, &sda_in_min[i].dev_attr);
@@ -1196,7 +1214,11 @@
 	client->flags = 0;
 	dev = &client->dev;
 
-	strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+	if (w83627ehf_num_in = 9)
+		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
+	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
+		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+
 	data->valid = 0;
 	mutex_init(&data->update_lock);
 
@@ -1246,7 +1268,7 @@
 				goto exit_remove;
 		}
 
-	for (i = 0; i < 10; i++)
+	for (i = 0; i < w83627ehf_num_in; i++)
 		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
 			|| (err = device_create_file(dev,
 				&sda_in_alarm[i].dev_attr))
@@ -1340,7 +1362,17 @@
 
 	val = (superio_inb(SIO_REG_DEVID) << 8)
 	    | superio_inb(SIO_REG_DEVID + 1);
-	if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
+	switch (val & SIO_ID_MASK) {
+	case SIO_W83627DHG_ID:
+		w83627ehf_num_in = 9;
+		break;
+	case SIO_W83627EHF_ID:
+	case SIO_W83627EHG_ID:
+		w83627ehf_num_in = 10;
+		break;
+	default:
+		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
+			val);
 		superio_exit();
 		return -ENODEV;
 	}
--- old/Documentation/hwmon/w83627ehf	2006-12-21 14:04:19.000000000 -0800
+++ new/Documentation/hwmon/w83627ehf	2006-12-24 21:55:46.000000000 -0800
@@ -2,26 +2,29 @@
 =========== 
 Supported chips:
-  * Winbond W83627EHF/EHG (ISA access ONLY)
+  * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
     Prefix: 'w83627ehf'
     Addresses scanned: ISA address retrieved from Super I/O registers
-    Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+    Datasheet:
+        http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+        DHG datasheet confidential.
 
 Authors:
         Jean Delvare <khali at linux-fr.org>
         Yuan Mu (Winbond)
         Rudolf Marek <r.marek at assembler.cz>
+        David Hubbard <david.c.hubbard at gmail.com>
 
 Description
 -----------
 
-This driver implements support for the Winbond W83627EHF and W83627EHG
-super I/O chips. We will refer to them collectively as Winbond chips.
+This driver implements support for the Winbond W83627EHF, W83627EHG, and
+W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
 
 The chips implement three temperature sensors, five fan rotation
-speed sensors, ten analog voltage sensors, alarms with beep warnings (control
-unimplemented), and some automatic fan regulation strategies (plus manual
-fan control mode).
+speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
+with beep warnings (control unimplemented), and some automatic fan regulation
+strategies (plus manual fan control mode).
 
 Temperatures are measured in degrees Celsius and measurement resolution is 1
 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@@ -55,6 +58,9 @@
 /sys files
 ----------
 
+name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
+       it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg"
+
 pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
 	   0 (stop) to 255 (full)
 
@@ -83,3 +89,36 @@
 
 Note: last two functions are influenced by other control bits, not yet exported
       by the driver, so a change might not have any effect.
+
+Implementation Details
+----------------------
+Future driver development should bear in mind that the following registers have
+different functions on the 627EHF and the 627DHG. Some registers also have
+different power-on default values, but BIOS should already be loading
+appropriate defaults. Note that bank selection must be performed as is currently
+done in the driver for all register addresses.
+
+0x49:  only on DHG, selects temperature source for AUX fan, CPU fan0
+0x4a:  not completely documented for the EHF and the DHG documentation assigns
+       different behavior to bits 7 and 6, including extending the temperature
+       input selection to SmartFan I, not just SmartFan III. Testing on the EHF
+       will reveal whether they are compatible or not.
+
+0x58:  Chip ID: 0xa1=EHF 0xc1=DHG
+0x5e:  only on DHG, has bits to enable "current mode" temperature detection and
+       critical temperature protection
+0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
+0x552: only on EHF, vin4
+0x558: only on EHF, vin4 high limit
+0x559: only on EHF, vin4 low limit
+0x6b:  only on DHG, SYS fan critical temperature
+0x6c:  only on DHG, CPU fan0 critical temperature
+0x6d:  only on DHG, AUX fan critical temperature
+0x6e:  only on DHG, CPU fan1 critical temperature
+
+0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
+       Register" for the DHG
+
+The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
+the ICH8 southbridge gets that data via PECI from the DHG, so that the
+southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
-------------- next part --------------
/*
    w83627ehf - Driver for the hardware monitoring functionality of
                the Winbond W83627EHF Super-I/O chip
    Copyright (C) 2005  Jean Delvare <khali at linux-fr.org>
    Copyright (C) 2006  Yuan Mu (Winbond),
                        Rudolf Marek <r.marek at assembler.cz>
                        David Hubbard <david.c.hubbard at gmail.com>

    Shamelessly ripped from the w83627hf driver
    Copyright (C) 2003  Mark Studebaker

    Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
    in testing and debugging this driver.

    This driver also supports the W83627EHG, which is the lead-free
    version of the W83627EHF.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


    Supports the following chips:

    Chip        #vin    #fan    #pwm    #temp  chip IDs       mfg ID
    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
                                               0x8860 0xa1
    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-isa.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <asm/io.h>
#include "lm75.h"

/* The actual ISA address is read from Super-I/O configuration space */
static unsigned short address;

/*
 * Super-I/O constants and functions
 */

/*
 * The three following globals are initialized in w83627ehf_find(), before
 * the i2c-isa device is created. Otherwise, they could be stored in
 * w83627ehf_data. This is ugly, but necessary. and when the driver is next
 * updated to become a platform driver, the globals will disappear.
 */
static int REG;		/* The register to read/write */
static int VAL;		/* The value to read/write */
/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
 * value is also used in w83627ehf_detect() to export a device name in sysfs
 * (e.g. w83627ehf or w83627dhg) */
static int w83627ehf_num_in;

#define W83627EHF_LD_HWM	0x0b

#define SIO_REG_LDSEL		0x07	/* Logical device select */
#define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
#define SIO_REG_ENABLE		0x30	/* Logical device enable */
#define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */

#define SIO_W83627EHF_ID	0x8850
#define SIO_W83627EHG_ID	0x8860
#define SIO_W83627DHG_ID	0xa020
#define SIO_ID_MASK		0xFFF0

static inline void
superio_outb(int reg, int val)
{
	outb(reg, REG);
	outb(val, VAL);
}

static inline int
superio_inb(int reg)
{
	outb(reg, REG);
	return inb(VAL);
}

static inline void
superio_select(int ld)
{
	outb(SIO_REG_LDSEL, REG);
	outb(ld, VAL);
}

static inline void
superio_enter(void)
{
	outb(0x87, REG);
	outb(0x87, REG);
}

static inline void
superio_exit(void)
{
	outb(0x02, REG);
	outb(0x02, VAL);
}

/*
 * ISA constants
 */

#define REGION_ALIGNMENT	~7
#define REGION_OFFSET		5
#define REGION_LENGTH		2
#define ADDR_REG_OFFSET		5
#define DATA_REG_OFFSET		6

#define W83627EHF_REG_BANK		0x4E
#define W83627EHF_REG_CONFIG		0x40

/* Not currently used:
 * REG_MAN_ID has the value 0x5ca3 for all supported chips.
 * REG_CHIP_ID = 0x88/0xa1/0xc1 depending on chip model.
 * REG_MAN_ID is at port 0x4f
 * REG_CHIP_ID is at port 0x58 */

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
static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };

/* Fan clock dividers are spread over the following five registers */
#define W83627EHF_REG_FANDIV1		0x47
#define W83627EHF_REG_FANDIV2		0x4B
#define W83627EHF_REG_VBAT		0x5D
#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

/* SmartFan registers */
/* DC or PWM output fan configuration */
static const u8 W83627EHF_REG_PWM_ENABLE[] = {
	0x04,			/* SYS FAN0 output mode and PWM mode */
	0x04,			/* CPU FAN0 output mode and PWM mode */
	0x12,			/* AUX FAN mode */
	0x62,			/* CPU fan1 mode */
};

static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };

/* FAN Duty Cycle, be used to control */
static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };


/* Advanced Fan control, some values are common for all fans */
static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };

/*
 * Conversions
 */

/* 1 is PWM mode, output in ms */
static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
{
	return mode ? 100 * reg : 400 * reg;
}

static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
{
	return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
						(msec + 200) / 400), 1, 255);
}

static inline unsigned int
fan_from_reg(u8 reg, unsigned int div)
{
	if (reg = 0 || reg = 255)
		return 0;
	return 1350000U / (reg * div);
}

static inline unsigned int
div_from_reg(u8 reg)
{
	return 1 << reg;
}

static inline int
temp1_from_reg(s8 reg)
{
	return reg * 1000;
}

static inline s8
temp1_to_reg(int temp, int min, int max)
{
	if (temp <= min)
		return min / 1000;
	if (temp >= max)
		return max / 1000;
	if (temp < 0)
		return (temp - 500) / 1000;
	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
 */

struct w83627ehf_data {
	struct i2c_client client;
	struct class_device *class_dev;
	struct mutex lock;

	struct mutex update_lock;
	char valid;		/* !=0 if following fields are valid */
	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];
	u8 has_fan;		/* some fan inputs can be disabled */
	s8 temp1;
	s8 temp1_max;
	s8 temp1_max_hyst;
	s16 temp[2];
	s16 temp_max[2];
	s16 temp_max_hyst[2];
	u32 alarms;

	u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
	u8 pwm_enable[4]; /* 1->manual
			     2->thermal cruise (also called SmartFan I) */
	u8 pwm[4];
	u8 target_temp[4];
	u8 tolerance[4];

	u8 fan_min_output[4]; /* minimum fan speed */
	u8 fan_stop_time[4];
};

static inline int is_word_sized(u16 reg)
{
	return (((reg & 0xff00) = 0x100
	      || (reg & 0xff00) = 0x200)
	     && ((reg & 0x00ff) = 0x50
	      || (reg & 0x00ff) = 0x53
	      || (reg & 0x00ff) = 0x55));
}

/* We assume that the default bank is 0, thus the following two functions do
   nothing for registers which live in bank 0. For others, they respectively
   set the bank register to the correct value (before the register is
   accessed), and back to 0 (afterwards). */
static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
{
	if (reg & 0xff00) {
		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
		outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
	}
}

static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
{
	if (reg & 0xff00) {
		outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
		outb_p(0, client->addr + DATA_REG_OFFSET);
	}
}

static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int res, word_sized = is_word_sized(reg);

	mutex_lock(&data->lock);

	w83627ehf_set_bank(client, reg);
	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
	res = inb_p(client->addr + DATA_REG_OFFSET);
	if (word_sized) {
		outb_p((reg & 0xff) + 1,
		       client->addr + ADDR_REG_OFFSET);
		res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
	}
	w83627ehf_reset_bank(client, reg);

	mutex_unlock(&data->lock);

	return res;
}

static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int word_sized = is_word_sized(reg);

	mutex_lock(&data->lock);

	w83627ehf_set_bank(client, reg);
	outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
	if (word_sized) {
		outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
		outb_p((reg & 0xff) + 1,
		       client->addr + ADDR_REG_OFFSET);
	}
	outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
	w83627ehf_reset_bank(client, reg);

	mutex_unlock(&data->lock);
	return 0;
}

/* This function assumes that the caller holds data->update_lock */
static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	u8 reg;

	switch (nr) {
	case 0:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
		    | ((data->fan_div[0] & 0x03) << 4);
		/* fan5 input control bit is write only, compute the value */
		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
		    | ((data->fan_div[0] & 0x04) << 3);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 1:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
		    | ((data->fan_div[1] & 0x03) << 6);
		/* fan5 input control bit is write only, compute the value */
		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
		    | ((data->fan_div[1] & 0x04) << 4);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 2:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
		    | ((data->fan_div[2] & 0x03) << 6);
		w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
		    | ((data->fan_div[2] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
		break;
	case 3:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
		    | (data->fan_div[3] & 0x03);
		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
		reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
		    | ((data->fan_div[3] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
		break;
	case 4:
		reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
		    | ((data->fan_div[4] & 0x03) << 3)
		    | ((data->fan_div[4] & 0x04) << 5);
		w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
		break;
	}
}

static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
	int i;

	mutex_lock(&data->update_lock);

	if (time_after(jiffies, data->last_updated + HZ)
	 || !data->valid) {
		/* Fan clock dividers */
		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
		data->fan_div[0] = (i >> 4) & 0x03;
		data->fan_div[1] = (i >> 6) & 0x03;
		i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
		data->fan_div[2] = (i >> 6) & 0x03;
		i = w83627ehf_read_value(client, W83627EHF_REG_VBAT);
		data->fan_div[0] |= (i >> 3) & 0x04;
		data->fan_div[1] |= (i >> 4) & 0x04;
		data->fan_div[2] |= (i >> 5) & 0x04;
		if (data->has_fan & ((1 << 3) | (1 << 4))) {
			i = w83627ehf_read_value(client, W83627EHF_REG_DIODE);
			data->fan_div[3] = i & 0x03;
			data->fan_div[4] = ((i >> 2) & 0x03)
					 | ((i >> 5) & 0x04);
		}
		if (data->has_fan & (1 << 3)) {
			i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT);
			data->fan_div[3] |= (i >> 5) & 0x04;
		}

		/* Measured voltages and limits */
		for (i = 0; i < w83627ehf_num_in; 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)))
				continue;

			data->fan[i] = w83627ehf_read_value(client,
				       W83627EHF_REG_FAN[i]);
			data->fan_min[i] = w83627ehf_read_value(client,
					   W83627EHF_REG_FAN_MIN[i]);

			/* If we failed to measure the fan speed and clock
			   divider can be increased, let's try that for next
			   time */
			if (data->fan[i] = 0xff
			 && data->fan_div[i] < 0x07) {
			 	dev_dbg(&client->dev, "Increasing fan %d "
					"clock divider from %u to %u\n",
					i, div_from_reg(data->fan_div[i]),
					div_from_reg(data->fan_div[i] + 1));
				data->fan_div[i]++;
				w83627ehf_write_fan_div(client, i);
				/* Preserve min limit if possible */
				if (data->fan_min[i] >= 2
				 && data->fan_min[i] != 255)
					w83627ehf_write_value(client,
						W83627EHF_REG_FAN_MIN[i],
						(data->fan_min[i] /= 2));
			}
		}

		for (i = 0; i < 4; i++) {
			/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
			if (i != 1) {
				pwmcfg = w83627ehf_read_value(client,
						W83627EHF_REG_PWM_ENABLE[i]);
				tolerance = w83627ehf_read_value(client,
						W83627EHF_REG_TOLERANCE[i]);
			}
			data->pwm_mode[i] 				((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
				? 0 : 1;
			data->pwm_enable[i] 					((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
						& 3) + 1;
			data->pwm[i] = w83627ehf_read_value(client,
						W83627EHF_REG_PWM[i]);
			data->fan_min_output[i] = w83627ehf_read_value(client,
						W83627EHF_REG_FAN_MIN_OUTPUT[i]);
			data->fan_stop_time[i] = w83627ehf_read_value(client,
						W83627EHF_REG_FAN_STOP_TIME[i]);
			data->target_temp[i] 				w83627ehf_read_value(client,
					W83627EHF_REG_TARGET[i]) &
					(data->pwm_mode[i] = 1 ? 0x7f : 0xff);
			data->tolerance[i] = (tolerance >> (i = 1 ? 4 : 0))
									& 0x0f;
		}

		/* Measured temperatures and limits */
		data->temp1 = w83627ehf_read_value(client,
			      W83627EHF_REG_TEMP1);
		data->temp1_max = w83627ehf_read_value(client,
				  W83627EHF_REG_TEMP1_OVER);
		data->temp1_max_hyst = w83627ehf_read_value(client,
				       W83627EHF_REG_TEMP1_HYST);
		for (i = 0; i < 2; i++) {
			data->temp[i] = w83627ehf_read_value(client,
					W83627EHF_REG_TEMP[i]);
			data->temp_max[i] = w83627ehf_read_value(client,
					    W83627EHF_REG_TEMP_OVER[i]);
			data->temp_max_hyst[i] = w83627ehf_read_value(client,
						 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;
	}

	mutex_unlock(&data->update_lock);
	return data;
}

/*
 * 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 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),
	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_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),
       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 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, "%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
show_fan_div(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", div_from_reg(data->fan_div[nr]));
}

static ssize_t
store_fan_min(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;
	unsigned int val = simple_strtoul(buf, NULL, 10);
	unsigned int reg;
	u8 new_div;

	mutex_lock(&data->update_lock);
	if (!val) {
		/* No min limit, alarm disabled */
		data->fan_min[nr] = 255;
		new_div = data->fan_div[nr]; /* No change */
		dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
	} else if ((reg = 1350000U / val) >= 128 * 255) {
		/* Speed below this value cannot possibly be represented,
		   even with the highest divider (128) */
		data->fan_min[nr] = 254;
		new_div = 7; /* 128 = (1 << 7) */
		dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
			 "minimum\n", nr + 1, val, fan_from_reg(254, 128));
	} else if (!reg) {
		/* Speed above this value cannot possibly be represented,
		   even with the lowest divider (1) */
		data->fan_min[nr] = 1;
		new_div = 0; /* 1 = (1 << 0) */
		dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
			 "maximum\n", nr + 1, val, fan_from_reg(1, 1));
	} else {
		/* Automatically pick the best divider, i.e. the one such
		   that the min limit will correspond to a register value
		   in the 96..192 range */
		new_div = 0;
		while (reg > 192 && new_div < 7) {
			reg >>= 1;
			new_div++;
		}
		data->fan_min[nr] = reg;
	}

	/* Write both the fan clock divider (if it changed) and the new
	   fan min (unconditionally) */
	if (new_div != data->fan_div[nr]) {
		if (new_div > data->fan_div[nr])
			data->fan[nr] >>= (data->fan_div[nr] - new_div);
		else
			data->fan[nr] <<= (new_div - data->fan_div[nr]);

		dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
			nr + 1, div_from_reg(data->fan_div[nr]),
			div_from_reg(new_div));
		data->fan_div[nr] = new_div;
		w83627ehf_write_fan_div(client, nr);
	}
	w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
			      data->fan_min[nr]);
	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_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),
	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_IRUGO, show_fan_div, NULL, 0),
	SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
	SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
	SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
	SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
};

#define show_temp1_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
	   char *buf) \
{ \
	struct w83627ehf_data *data = w83627ehf_update_device(dev); \
	return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
}
show_temp1_reg(temp1);
show_temp1_reg(temp1_max);
show_temp1_reg(temp1_max_hyst);

#define store_temp1_reg(REG, reg) \
static ssize_t \
store_temp1_##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); \
	u32 val = simple_strtoul(buf, NULL, 10); \
 \
	mutex_lock(&data->update_lock); \
	data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
	w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
			      data->temp1_##reg); \
	mutex_unlock(&data->update_lock); \
	return count; \
}
store_temp1_reg(OVER, max);
store_temp1_reg(HYST, max_hyst);

#define show_temp_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, "%d\n", \
		       LM75_TEMP_FROM_REG(data->reg[nr])); \
}
show_temp_reg(temp);
show_temp_reg(temp_max);
show_temp_reg(temp_max_hyst);

#define store_temp_reg(REG, reg) \
static ssize_t \
store_##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->reg[nr] = LM75_TEMP_TO_REG(val); \
	w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
			      data->reg[nr]); \
	mutex_unlock(&data->update_lock); \
	return count; \
}
store_temp_reg(OVER, temp_max);
store_temp_reg(HYST, temp_max_hyst);

static struct sensor_device_attribute sda_temp[] = {
	SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
	SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
	SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
	SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
		    store_temp1_max, 0),
	SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
		    store_temp_max, 0),
	SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
		    store_temp_max, 1),
	SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
		    store_temp1_max_hyst, 0),
	SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
		    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),
};

#define show_pwm_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, "%d\n", data->reg[nr]); \
}

show_pwm_reg(pwm_mode)
show_pwm_reg(pwm_enable)
show_pwm_reg(pwm)

static ssize_t
store_pwm_mode(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);
	u16 reg;

	if (val > 1)
		return -EINVAL;
	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
	data->pwm_mode[nr] = val;
	reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
	if (!val)
		reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_pwm(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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);

	mutex_lock(&data->update_lock);
	data->pwm[nr] = val;
	w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_pwm_enable(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);
	u16 reg;

	if (!val || (val > 2))	/* only modes 1 and 2 are supported */
		return -EINVAL;
	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
	data->pwm_enable[nr] = val;
	reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
	reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
	w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}


#define show_tol_temp(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, "%d\n", temp1_from_reg(data->reg[nr])); \
}

show_tol_temp(tolerance)
show_tol_temp(target_temp)

static ssize_t
store_target_temp(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;
	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);

	mutex_lock(&data->update_lock);
	data->target_temp[nr] = val;
	w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
	mutex_unlock(&data->update_lock);
	return count;
}

static ssize_t
store_tolerance(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;
	u16 reg;
	/* Limit the temp to 0C - 15C */
	u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);

	mutex_lock(&data->update_lock);
	reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
	data->tolerance[nr] = val;
	if (nr = 1)
		reg = (reg & 0x0f) | (val << 4);
	else
		reg = (reg & 0xf0) | val;
	w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
	mutex_unlock(&data->update_lock);
	return count;
}

static struct sensor_device_attribute sda_pwm[] = {
	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),
	SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
};

static struct sensor_device_attribute sda_pwm_mode[] = {
	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 0),
	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 1),
	SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 2),
	SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
		    store_pwm_mode, 3),
};

static struct sensor_device_attribute sda_pwm_enable[] = {
	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 0),
	SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 1),
	SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 2),
	SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
		    store_pwm_enable, 3),
};

static struct sensor_device_attribute sda_target_temp[] = {
	SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 0),
	SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 1),
	SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 2),
	SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
		    store_target_temp, 3),
};

static struct sensor_device_attribute sda_tolerance[] = {
	SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 0),
	SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 1),
	SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 2),
	SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
		    store_tolerance, 3),
};

/* Smart Fan registers */

#define fan_functions(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, "%d\n", data->reg[nr]); \
}\
static ssize_t \
store_##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 = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
	mutex_lock(&data->update_lock); \
	data->reg[nr] = val; \
	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr],  val); \
	mutex_unlock(&data->update_lock); \
	return count; \
}

fan_functions(fan_min_output, FAN_MIN_OUTPUT)

#define fan_time_functions(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, "%d\n", \
			step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
} \
\
static ssize_t \
store_##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; \
	u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
					data->pwm_mode[nr]); \
	mutex_lock(&data->update_lock); \
	data->reg[nr] = val; \
	w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
	mutex_unlock(&data->update_lock); \
	return count; \
} \

fan_time_functions(fan_stop_time, FAN_STOP_TIME)


static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
	SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 3),
	SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 3),
};

static struct sensor_device_attribute sda_sf3_arrays[] = {
	SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 0),
	SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 1),
	SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
		    store_fan_stop_time, 2),
	SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 0),
	SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 1),
	SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
		    store_fan_min_output, 2),
};

/*
 * Driver and client management
 */

static void w83627ehf_device_remove_files(struct device *dev)
{
	/* some entries in the following arrays may not have been used in
	 * device_create_file(), but device_remove_file() will ignore them */
	int i;

	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
		device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
		device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
	for (i = 0; i < w83627ehf_num_in; i++) {
		device_remove_file(dev, &sda_in_input[i].dev_attr);
		device_remove_file(dev, &sda_in_alarm[i].dev_attr);
		device_remove_file(dev, &sda_in_min[i].dev_attr);
		device_remove_file(dev, &sda_in_max[i].dev_attr);
	}
	for (i = 0; i < 5; i++) {
		device_remove_file(dev, &sda_fan_input[i].dev_attr);
		device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
		device_remove_file(dev, &sda_fan_div[i].dev_attr);
		device_remove_file(dev, &sda_fan_min[i].dev_attr);
	}
	for (i = 0; i < 4; i++) {
		device_remove_file(dev, &sda_pwm[i].dev_attr);
		device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
		device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
		device_remove_file(dev, &sda_target_temp[i].dev_attr);
		device_remove_file(dev, &sda_tolerance[i].dev_attr);
	}
	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
		device_remove_file(dev, &sda_temp[i].dev_attr);
}

static struct i2c_driver w83627ehf_driver;

static void w83627ehf_init_client(struct i2c_client *client)
{
	int i;
	u8 tmp;

	/* Start monitoring is needed */
	tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
	if (!(tmp & 0x01))
		w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
				      tmp | 0x01);

	/* Enable temp2 and temp3 if needed */
	for (i = 0; i < 2; i++) {
		tmp = w83627ehf_read_value(client,
					   W83627EHF_REG_TEMP_CONFIG[i]);
		if (tmp & 0x01)
			w83627ehf_write_value(client,
					      W83627EHF_REG_TEMP_CONFIG[i],
					      tmp & 0xfe);
	}
}

static int w83627ehf_detect(struct i2c_adapter *adapter)
{
	struct i2c_client *client;
	struct w83627ehf_data *data;
	struct device *dev;
	u8 fan4pin, fan5pin;
	int i, err = 0;

	if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
	                    w83627ehf_driver.driver.name)) {
		err = -EBUSY;
		goto exit;
	}

	if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
		err = -ENOMEM;
		goto exit_release;
	}

	client = &data->client;
	i2c_set_clientdata(client, data);
	client->addr = address;
	mutex_init(&data->lock);
	client->adapter = adapter;
	client->driver = &w83627ehf_driver;
	client->flags = 0;
	dev = &client->dev;

	if (w83627ehf_num_in = 9)
		strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
	else	/* just say ehf. 627EHG is 627EHF in lead-free packaging. */
		strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);

	data->valid = 0;
	mutex_init(&data->update_lock);

	/* Tell the i2c layer a new client has arrived */
	if ((err = i2c_attach_client(client)))
		goto exit_free;

	/* Initialize the chip */
	w83627ehf_init_client(client);

	/* A few vars need to be filled upon startup */
	for (i = 0; i < 5; i++)
		data->fan_min[i] = w83627ehf_read_value(client,
				   W83627EHF_REG_FAN_MIN[i]);

	/* fan4 and fan5 share some pins with the GPIO and serial flash */

	superio_enter();
	fan5pin = superio_inb(0x24) & 0x2;
	fan4pin = superio_inb(0x29) & 0x6;
	superio_exit();

	/* It looks like fan4 and fan5 pins can be alternatively used
	   as fan on/off switches, but fan5 control is write only :/
	   We assume that if the serial interface is disabled, designers
	   connected fan5 as input unless they are emitting log 1, which
	   is not the default. */

	data->has_fan = 0x07; /* fan1, fan2 and fan3 */
	i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
	if ((i & (1 << 2)) && (!fan4pin))
		data->has_fan |= (1 << 3);
	if (!(i & (1 << 1)) && (!fan5pin))
		data->has_fan |= (1 << 4);

	/* Register sysfs hooks */
  	for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
		if ((err = device_create_file(dev,
			&sda_sf3_arrays[i].dev_attr)))
			goto exit_remove;

	/* if fan4 is enabled create the sf3 files for it */
	if (data->has_fan & (1 << 3))
		for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
			if ((err = device_create_file(dev,
				&sda_sf3_arrays_fan4[i].dev_attr)))
				goto exit_remove;
		}

	for (i = 0; i < w83627ehf_num_in; i++)
		if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_alarm[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_min[i].dev_attr))
			|| (err = device_create_file(dev,
				&sda_in_max[i].dev_attr)))
			goto exit_remove;

	for (i = 0; i < 5; i++) {
		if (data->has_fan & (1 << i)) {
			if ((err = device_create_file(dev,
					&sda_fan_input[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_alarm[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_div[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_fan_min[i].dev_attr)))
				goto exit_remove;
			if (i < 4 && /* w83627ehf only has 4 pwm */
				((err = device_create_file(dev,
					&sda_pwm[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_pwm_mode[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_pwm_enable[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_target_temp[i].dev_attr))
				|| (err = device_create_file(dev,
					&sda_tolerance[i].dev_attr))))
				goto exit_remove;
		}
	}

	for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
		if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
			goto exit_remove;

	data->class_dev = hwmon_device_register(dev);
	if (IS_ERR(data->class_dev)) {
		err = PTR_ERR(data->class_dev);
		goto exit_remove;
	}

	return 0;

exit_remove:
	w83627ehf_device_remove_files(dev);
	i2c_detach_client(client);
exit_free:
	kfree(data);
exit_release:
	release_region(address + REGION_OFFSET, REGION_LENGTH);
exit:
	return err;
}

static int w83627ehf_detach_client(struct i2c_client *client)
{
	struct w83627ehf_data *data = i2c_get_clientdata(client);
	int err;

	hwmon_device_unregister(data->class_dev);
	w83627ehf_device_remove_files(&client->dev);

	if ((err = i2c_detach_client(client)))
		return err;
	release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
	kfree(data);

	return 0;
}

static struct i2c_driver w83627ehf_driver = {
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "w83627ehf",
	},
	.attach_adapter	= w83627ehf_detect,
	.detach_client	= w83627ehf_detach_client,
};

static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
{
	u16 val;

	REG = sioaddr;
	VAL = sioaddr + 1;
	superio_enter();

	val = (superio_inb(SIO_REG_DEVID) << 8)
	    | superio_inb(SIO_REG_DEVID + 1);
	switch (val & SIO_ID_MASK) {
	case SIO_W83627DHG_ID:
		w83627ehf_num_in = 9;
		break;
	case SIO_W83627EHF_ID:
	case SIO_W83627EHG_ID:
		w83627ehf_num_in = 10;
		break;
	default:
		printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
			val);
		superio_exit();
		return -ENODEV;
	}

	superio_select(W83627EHF_LD_HWM);
	val = (superio_inb(SIO_REG_ADDR) << 8)
	    | superio_inb(SIO_REG_ADDR + 1);
	*addr = val & REGION_ALIGNMENT;
	if (*addr = 0) {
		superio_exit();
		return -ENODEV;
	}

	/* Activate logical device if needed */
	val = superio_inb(SIO_REG_ENABLE);
	if (!(val & 0x01))
		superio_outb(SIO_REG_ENABLE, val | 0x01);

	superio_exit();
	return 0;
}

static int __init sensors_w83627ehf_init(void)
{
	if (w83627ehf_find(0x2e, &address)
	 && w83627ehf_find(0x4e, &address))
		return -ENODEV;

	return i2c_isa_add_driver(&w83627ehf_driver);
}

static void __exit sensors_w83627ehf_exit(void)
{
	i2c_isa_del_driver(&w83627ehf_driver);
}

MODULE_AUTHOR("Jean Delvare <khali at linux-fr.org>");
MODULE_DESCRIPTION("W83627EHF driver");
MODULE_LICENSE("GPL");

module_init(sensors_w83627ehf_init);
module_exit(sensors_w83627ehf_exit);

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (27 preceding siblings ...)
  2007-02-07 18:41 ` David Hubbard
@ 2007-02-08 19:44 ` Rudolf Marek
  2007-02-12  9:04 ` Brett King
  29 siblings, 0 replies; 31+ messages in thread
From: Rudolf Marek @ 2007-02-08 19:44 UTC (permalink / raw)
  To: lm-sensors

Hello again

Sorry for the delay.
> I figured out why I got zero readings for fan5.  Since my case fans are 
> lower speed, it seems the divisor was to low.  I remedied this by 
> settting fan1_min and fan5_min to 700.  This also has the side effect of 
> setting the divisor to 16.  Now I get readings in the 1100 RPM range for 
> both fan1 and fan5.

Ok. That explains it.

> Here is what I see in the BIOS Hardware Monitor after being on for about 
> 5 minutes:
> 
> CPU Temperature          35.5 C
> MB Temperature           21 C
> CPU Fan Speed            2556
> CPU QFan Control         Enabled
> CPU Fan Profile          Optimal
> Chassis Fan1 Speed       1095
> Chassis Fan2 Speed       1163
> Chassis Fan3 Speed       N/A
> Chassis QFan Control     Enabled
> Chassis Fan Profile      Optimal
> Power Fan Speed          N/A
> 
> VCore Voltage             1.176
> 3.3V Voltage              3.328
> 5V Voltage                4.992
> 12V Voltage              12.144
> 
> Here is my Idle sensors output
> 
> jharvell at cujo ~ $ sensors
> w83627dhg-isa-0290
> Adapter: ISA adapter
> VCore:     +1.12 V  (min =  +0.00 V, max =  +1.74 V)
> 12 V:     +12.14 V  (min = +10.82 V, max = +13.20 V)
> 3.3V:      +3.33 V  (min =  +3.14 V, max =  +3.47 V)
> 5V:        +5.04 V  (min =  +4.76 V, max =  +5.25 V)
> VSB:       +3.33 V  (min =  +3.14 V, max =  +3.47 V)
> CHA_FAN1: 1125 RPM  (min =  703 RPM, div = 16)
> CPU_FAN:  2518 RPM  (min = 1704 RPM, div = 8)
> CHA_FAN2: 1222 RPM  (min =  703 RPM, div = 16)
> Sys Temp:    +28 C  (high =   +40 C, hyst =   +35 C)
> CPU Temp:  +34.5 C  (high = +55.0 C, hyst = +45.0 C)
> 
> Actually, I recently finished a compile, so it may still be cooling 
> slightly.  Also, I am using CPU Frequency scaling.  So the CPU is at 
> 1.60 GHz instead of the normal 2.39 GHz.  I don't know what the 
> operating CPU frequency is in the BIOS.
> 
> jharvell at cujo ~ $ cpufreq-info
> cpufrequtils 002: cpufreq-info (C) Dominik Brodowski 2004-2006
> Report errors and bugs to linux at brodo.de, please.
> analyzing CPU 0:
>   driver: centrino
>   CPUs which need to switch frequency at the same time: 0
>   hardware limits: 1.60 GHz - 2.39 GHz
>   available frequency steps: 2.39 GHz, 1.60 GHz
>   available cpufreq governors: ondemand, performance
>   current policy: frequency should be within 1.60 GHz and 2.39 GHz.
>                   The governor "ondemand" may decide which speed to use
>                   within this range.
>   current CPU frequency is 1.60 GHz.
> analyzing CPU 1:
>   driver: centrino
>   CPUs which need to switch frequency at the same time: 1
>   hardware limits: 1.60 GHz - 2.39 GHz
>   available frequency steps: 2.39 GHz, 1.60 GHz
>   available cpufreq governors: ondemand, performance
>   current policy: frequency should be within 1.60 GHz and 2.39 GHz.
>                   The governor "ondemand" may decide which speed to use
>                   within this range.
>   current CPU frequency is 1.60 GHz.

Maybe you can also try the coretemp driver if the CPU is pentium M or Core series.

Rudolf


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG
  2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
                   ` (28 preceding siblings ...)
  2007-02-08 19:44 ` Rudolf Marek
@ 2007-02-12  9:04 ` Brett King
  29 siblings, 0 replies; 31+ messages in thread
From: Brett King @ 2007-02-12  9:04 UTC (permalink / raw)
  To: lm-sensors

Success !
Thanks a great deal David, I couldn't have got this working without
your expertise - you are indeed a credit to the community.

# modprobe w83627ehf
# sensors
w83627dhg-isa-0290
Adapter: ISA adapter
in0:       +1.26 V  (min =  +0.00 V, max =  +1.74 V)
in1:       +1.86 V  (min =  +1.30 V, max =  +0.30 V) ALARM
in2:       +3.30 V  (min =  +0.54 V, max =  +0.56 V) ALARM
in3:       +3.30 V  (min =  +2.26 V, max =  +2.08 V) ALARM
in4:       +1.99 V  (min =  +0.08 V, max =  +0.06 V) ALARM
in5:       +1.62 V  (min =  +1.10 V, max =  +0.56 V) ALARM
in6:       +1.98 V  (min =  +0.00 V, max =  +1.18 V) ALARM
in7:       +3.30 V  (min =  +2.21 V, max =  +2.08 V) ALARM
in8:       +1.28 V  (min =  +2.05 V, max =  +0.03 V) ALARM
fan1:        0 RPM  (min = 12500 RPM, div = 4) ALARM
fan2:     2235 RPM  (min = 2986 RPM, div = 4) ALARM
fan3:        0 RPM  (min = 10227 RPM, div = 4) ALARM
fan4:        0 RPM  (min = 225000 RPM, div = 2) ALARM
fan5:        0 RPM  (min = 4821 RPM, div = 4) ALARM
temp1:       +34?C  (high =   +73?C, hyst =   +42?C)
temp2:     +54.5?C  (high = +80.0?C, hyst = +75.0?C)
temp3:    +124.5?C  (high = +80.0?C, hyst = +75.0?C)   ALARM

#

Cheers.

On 2/8/07, David Hubbard <david.c.hubbard at gmail.com> wrote:
> Hi Brett,
>
> I am sending you the patch and the full driver.
>
> It's actually pretty easy to use the patch. You just cd into your
> kernel directory, say /usr/src/linux-2.6.18
>
> # cd /usr/src/linux-2.6.18
> linux-2.6.18 #
>
> Then you run 'patch' like this:
>
> linux-2.6.18 # patch -p1 -i
> /home/yourdirectory/somepath/w83627ehf_add_w83627dhg_support.patch.txt
>
> It will tell you it is patching drivers/hwmon/w83627ehf.c and then you
> have the driver installed and ready to go.
>
> linux-2.6.18 # make
> linux-2.6.18 # make modules_install
>
> Any other kernel compile steps would go here. If you are lucky, you
> won't even need to reboot. The new module will overwrite the old
> w83627ehf.ko and you can just load the new module like this:
>
> linux-2.6.18 # modprobe w83627ehf
>
>
> Finally, the original patch is at this link with all the same information.
> http://lists.lm-sensors.org/pipermail/lm-sensors/2006-December/018519.html
>
> Hope that helps,
> David
>
>


^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2007-02-12  9:04 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-12-21 21:13 [lm-sensors] Asus P5B Deluxe / Winbond 83627DHG David Hubbard
2006-12-22 14:58 ` Jean Delvare
2006-12-25  4:58 ` David Hubbard
2006-12-25 18:23 ` Jean Delvare
2006-12-26  8:02 ` David Hubbard
2006-12-26 10:31 ` Hamlet
2006-12-26 23:14 ` David Hubbard
2006-12-27  1:10 ` Hamlet
2006-12-28 22:53 ` Hamlet
2006-12-29  2:35 ` David Hubbard
2006-12-29 14:37 ` Hamlet
2006-12-30  2:54 ` David Hubbard
2007-01-02 18:46 ` Rudolf Marek
2007-01-02 18:46 ` David Holl
2007-01-19  6:02 ` Daniel Ceregatti
2007-01-19  6:31 ` David Hubbard
2007-01-19  7:42 ` David Hubbard
2007-01-19 22:04 ` David Holl
2007-01-19 22:11 ` Daniel Ceregatti
2007-01-19 22:27 ` David Holl
2007-01-20  5:37 ` Daniel Ceregatti
2007-01-20 19:01 ` David Hubbard
2007-01-22 15:51 ` David Holl
2007-01-31  1:36 ` Joe Harvell
2007-02-04 16:49 ` Rudolf Marek
2007-02-05 18:43 ` Joe Harvell
2007-02-06  3:54 ` Joe Harvell
2007-02-06 17:11 ` Joe Harvell
2007-02-07 18:41 ` David Hubbard
2007-02-08 19:44 ` Rudolf Marek
2007-02-12  9:04 ` Brett King

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.