Hello,

It is my pleasure to finally announce the availability of the port of the fan-controlling prototype/hack from OpenBSD to DragonFly BSD, which allows interested users to monitor and control the speed of the fans connected to popular Winbond Super I/O Hardware Monitors.

The patch is a direct port of the patch that was written for OpenBSD and published for BSDCan 2009 in 2009-05-08 on the t...@openbsd.org mailing list, and supports the following chips, as shown by their lm(4) dmesg name:
* W83627HF (chip only provides the manual PWM mode, fully supported)
* W83627THF / W83637HF (manual and thermal cruise modes are supported)
* W83627EHF / W83627DHG (manual and thermal cruise, PWM/DC supported)

The list above identifies 3 families, with the latter family having the most functionality.

Note that Winbond often shares the Hardware Monitor Chip ID between Super I/O solutions that may be marked differently on the Super I/O chip itself, so in practicality, each family includes more than the one or two chips outlined above.

If you know you have a Winbond Super I/O chip similar to the ones mentioned above, but the lm(4) driver never probed successfully on DragonFly, then please note that since recently we have a new wbsio(4) driver in DragonFly, which allows the lm(4) attachment on non-standard ports (e.g. non-0x290 port). The wbsio(4) is now also available as a module, which, when loaded, will automatically configure lm(4), whether a module or in-kernel, at the proper address. The wbsio(4) driver will also be more specific as to which Winbond Super I/O device and revision your mainboard has.

I will describe the functionality of the latter family, W83627EHF and W83627DHG, which has the most features, and some parts of which will not be applicable to the earlier families.

The percent{0,1,2,3} sensors provide a summary of what the current settings of the chip are. The value shows the current duty cycle, whereas the description specifies whether the PWM or the DC mode is currently activated, and what kind of controlling is being done: Manual, Thermal Cruise, Fan Speed Cruise or Smart Fan III.

If you change any of the percent{0,1,2,3} sensors (integer values only), the chip will automatically go from whatever mode on that specific fan output was, to the Manual mode, with the duty cycle set to the value that you specify.

If you want to go into the Thermal Cruise mode, simply set the value of one of the temp sensors that have a "Target" in their description to the target temperature that you desire. In Thermal Cruise mode, you could also modify the Start-up and Stop duty cycle of the fan, and, of course, the target temperature tolerance.

PWM/DC mode setting is done by switching the indicators between 0 and 1. The description of the indicator indicates which mode is currently active for which fan. If your fan doesn't seem to stop at all with one mode, try the other.

I'd like to warn users that many motherboards are terribly miswired as far as fan-controlling is concerned, so please don't be surprised if the controlling doesn't seem to affect the speed of the fans, or if one control affects the speed of multiple fans etc.

Please note that should you wish to reset any settings that you have programmed your Winbond Super I/O Hardware Monitor with, you may wish to power your system off (obviously, the system may not necessarily go off if you simply `shutdown -p now` or press the soft power button on the front of your PC).

More details and the presentation materials are available on my website dedicated to the fan control project:
   http://sensors.cnst.su/fanctl/

This work has recently been presented at AsiaBSDCon 2010 in Tokyo, Japan, and I'd like to thank Hiroki Sato for the wonderful conference that he brings about!

This patch is also available in my git repository on leaf (git://leaf.dragonflybsd.org/~cnst/dragonfly.git), in the fanctl and fanctl201003 branches.


Please test. In your report, please include the name and brand of the board, the full dmesg, sysctl hw.sensors output before making any changes, and a report of your experience as appropriate. Don't be shy.


Some testing suggestions to see if your system can actually control the fans through the lm(4) driver (don't forget to save the whole sensors tree to recall what the defaults were before issuing this):
%sysctl hw.sensors.lm0.percent{0,1,2,3}=0 && echo manually stop the fans
%sysctl hw.sensors.lm0.percent{0,1,2,3}=100 && echo manually max the fans


You can also try this, provided temp{3,4,5,6} have Target in their description:
%sysctl hw.sensors.lm0.temp{3,4,5,6}=65 && echo automatically stop the fans
%sysctl hw.sensors.lm0.temp{3,4,5,6}=24 && echo automatically max the fans


To log the changes in the temperature when doing the above tests:
sh -c "while(true)do tcsh -c 'sysctl -n 
hw.sensors.lm0.{percent{0,1,2,3},{temp,fan}{0,1,2}}'|xargs;sleep 3;done"


Do note again that in many cases only the setting of one of the respective fan control pins may actually be affecting all of the fans on your system. Please be kind enough to report any findings.

Best regards,
Constantine.
>From 95889133f248d52da7be4610d86d7dbd61f4577c Mon Sep 17 00:00:00 2001
From: Constantine A. Murenin <cnst+d...@bugmail.mojo.ru>
Date: Fri, 12 Mar 2010 03:21:14 -0500
Subject: [PATCH] fanctl: AsiaBSDCon 2010 DragonFly sysctl hw.sensors lm(4) fan 
control similar to BSDCan 2009 OpenBSD

* This is a ported version of the sysctl hw.sensors lm(4)
  fan-controlling prototype/hack as posted to the t...@openbsd.org
  mailing list on 2009-05-08.

* DragonFly functionality regarding all aspects of fan control
  is the same as discussed for OpenBSD.

* Details were presented at BSDCan 2009 and AsiaBSDCon 2010.

* Presentation and other materials are available at
    http://sensors.cnst.su/fanctl/
---
 sys/sys/sensors.h             |   11 +
 sys/kern/kern_sensors.c       |   14 +-
 sbin/sysctl/sysctl.c          |    8 +
 sys/dev/powermng/lm/lm78var.h |    2 +-
 sys/dev/powermng/lm/lm78.c    |  753 +++++++++++++++++++++++++++++++++++++++--
 5 files changed, 761 insertions(+), 27 deletions(-)

diff --git a/sys/sys/sensors.h b/sys/sys/sensors.h
index 554cfc2..ca1e46d 100644
--- a/sys/sys/sensors.h
+++ b/sys/sys/sensors.h
@@ -97,18 +97,28 @@ enum sensor_status {
 struct sensor {
        char desc[32];                  /* sensor description, may be empty */
        struct timeval tv;              /* sensor value last change time */
        int64_t value;                  /* current value */
        enum sensor_type type;          /* sensor type */
        enum sensor_status status;      /* sensor status */
        int numt;                       /* sensor number of .type type */
        int flags;                      /* sensor flags */
+       /*int64_t upvalue;*/            /* new value */
+       /*
+        * The upvalue is commented out from the userland structure
+        * to avoid increasing sizeof(struct sensor), such as to
+        * preserve the ABI of C/C++ sysctl(3) HW_SENSORS users,
+        * since otherwise recompilation of all sensor tools would
+        * have been required to avoid [ENOMEM] from sysctl(3).
+        */
 #define SENSOR_FINVALID                0x0001  /* sensor is invalid */
 #define SENSOR_FUNKNOWN                0x0002  /* sensor value is unknown */
+#define SENSOR_FCONTROLLABLE   0x0004  /* sensor value could be altered */
+#define SENSOR_FNEWVALUE       0x0008  /* upvalue contains update inf. */
 };
 
 /* Sensor device data:
  * New fields should be added at the end to encourage backwards compat
  */
 struct sensordev {
        int num;                        /* sensordev number */
        char xname[16];                 /* unix device name */
@@ -129,16 +139,17 @@ struct ksensor {
        SLIST_ENTRY(ksensor) list;      /* device-scope list */
        char desc[32];                  /* sensor description, may be empty */
        struct timeval tv;              /* sensor value last change time */
        int64_t value;                  /* current value */
        enum sensor_type type;          /* sensor type */
        enum sensor_status status;      /* sensor status */
        int numt;                       /* sensor number of .type type */
        int flags;                      /* sensor flags, ie. SENSOR_FINVALID */
+       int64_t upvalue;                /* new value */
 };
 SLIST_HEAD(ksensors_head, ksensor);
 
 /* Sensor device data */
 struct ksensordev {
        SLIST_ENTRY(ksensordev) list;
        int num;                        /* sensordev number */
        char xname[16];                 /* unix device name */
diff --git a/sys/kern/kern_sensors.c b/sys/kern/kern_sensors.c
index b8794a1..8cb6c7a 100644
--- a/sys/kern/kern_sensors.c
+++ b/sys/kern/kern_sensors.c
@@ -317,17 +317,18 @@ sensor_sysctl8magic_install(struct ksensordev *sensdev)
        sysctl_ctx_init(cl);
        ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw,
            sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, ""));
        SLIST_FOREACH(s, sh, list) {
                char n[32];
 
                ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], 
s->numt);
                SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT |
-                   CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", "");
+                   (s->flags & SENSOR_FCONTROLLABLE ? CTLFLAG_RW : CTLFLAG_RD),
+                   s, 0, sysctl_handle_sensor, "S,sensor", "");
        }
 }
 
 void
 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev)
 {
        struct sysctl_ctx_list *cl = &sensdev->clist;
 
@@ -363,28 +364,35 @@ sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
 
 int
 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
 {
        struct ksensor *ks = arg1;
        struct sensor *us;
        int error;
 
-       if (req->newptr)
-               return (EPERM);
+       if (req->newptr) {
+               if (req->newlen != sizeof(int))
+                       return EINVAL;
+               if (!(ks->flags & SENSOR_FCONTROLLABLE))
+                       return EPERM;
+               ks->upvalue = *(int *)req->newptr;
+               ks->flags |= SENSOR_FNEWVALUE;
+       }
 
        /* Grab a copy, to clear the kernel pointers */
        us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO);
        memcpy(us->desc, ks->desc, sizeof(ks->desc));
        us->tv = ks->tv;
        us->value = ks->value;
        us->type = ks->type;
        us->status = ks->status;
        us->numt = ks->numt;
        us->flags = ks->flags;
+       /*us->upvalue = ks->upvalue;*/
 
        error = SYSCTL_OUT(req, us, sizeof(struct sensor));
 
        kfree(us, M_TEMP);
        return (error);
 }
 
 int
diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c
index f26613f..18e82f5 100644
--- a/sbin/sysctl/sysctl.c
+++ b/sbin/sysctl/sysctl.c
@@ -245,16 +245,21 @@ parse(const char *string)
                                break;
                        case CTLTYPE_OPAQUE:
                                if (strcmp(fmt, "T,dev_t") == 0 ||
                                    strcmp(fmt, "T,udev_t") == 0
                                ) {
                                        set_T_dev_t((char*)newval, &newval,
                                                    &newsize);
                                        break;
+                               } else if (strcmp(fmt, "S,sensor") == 0) {
+                                       intval = (int)strtol(newval, NULL, 0);
+                                       newval = &intval;
+                                       newsize = sizeof(intval);
+                                       break;
                                }
                                /* FALLTHROUGH */
                        default:
                                errx(1, "oid '%s' is type %d,"
                                        " cannot set that", name,
                                        kind & CTLTYPE);
                }
 
@@ -459,16 +464,19 @@ S_sensor(int l2, void *p)
                case SENSOR_TIMEDELTA:
                        printf("%.6f secs", s->value / 1000000000.0);
                        break;
                default:
                        printf("unknown");
                }
        }
 
+       if (s->flags & SENSOR_FNEWVALUE)
+               printf(" {updating}");
+
        if (s->desc[0] != '\0')
                printf(" (%s)", s->desc);
 
        switch (s->status) {
        case SENSOR_S_UNSPEC:
                break;
        case SENSOR_S_OK:
                printf(", OK");
diff --git a/sys/dev/powermng/lm/lm78var.h b/sys/dev/powermng/lm/lm78var.h
index 67438ee..648feca 100644
--- a/sys/dev/powermng/lm/lm78var.h
+++ b/sys/dev/powermng/lm/lm78var.h
@@ -118,17 +118,17 @@
 
 /* Config bits */
 #define WB_CONFIG_VMR9         0x01
 
 /* Reference voltage (mV) */
 #define WB_VREF                        3600
 #define WB_W83627EHF_VREF      2048
 
-#define WB_MAX_SENSORS  19
+#define WB_MAX_SENSORS  96
 
 struct lm_softc;
 
 struct lm_sensor {
        char *desc;
        enum sensor_type type;
        u_int8_t bank;
        u_int8_t reg;
diff --git a/sys/dev/powermng/lm/lm78.c b/sys/dev/powermng/lm/lm78.c
index af74d9e..dc348ba 100644
--- a/sys/dev/powermng/lm/lm78.c
+++ b/sys/dev/powermng/lm/lm78.c
@@ -1,11 +1,11 @@
 /*
  * Copyright (c) 2005, 2006 Mark Kettenis
- * Copyright (c) 2006, 2007 Constantine A. Murenin
+ * Copyright (c) 2006, 2007, 2009, 2010 Constantine A. Murenin <cnst>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
@@ -19,16 +19,20 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 #include <sys/sensors.h>
 
 #include "lm78var.h"
 
+//#define LMFANCTLRAW
+#define LMFANDUTYHINTS
+/* or use `rm lm78.o; env DEBUG="-DLMFANCTLRAW -DLMFANDUTYHINTS" make lm78.o` 
*/
+
 #if defined(LMDEBUG)
 #define DPRINTF(x)             do { printf x; } while (0)
 #else
 #define DPRINTF(x)
 #endif
 
 /*
  * LM78-compatible chips can typically measure voltages up to 4.096 V.
@@ -49,16 +53,24 @@ void lm_setup_sensors(struct lm_softc *, struct lm_sensor 
*);
 void lm_refresh(void *);
 
 void lm_refresh_sensor_data(struct lm_softc *);
 void lm_refresh_volt(struct lm_softc *, int);
 void lm_refresh_temp(struct lm_softc *, int);
 void lm_refresh_fanrpm(struct lm_softc *, int);
 
 void wb_refresh_sensor_data(struct lm_softc *);
+void wb_refresh_raw_rw(struct lm_softc *, int);
+void w83627hf_refresh_pwm_rw(struct lm_softc *, int);
+void w83627ehf_refresh_fanvolt_rw(struct lm_softc *, int);
+void w83627ehf_refresh_fanvolt_thermal_rw(struct lm_softc *, int);
+void w83627ehf_refresh_indicator_rw(struct lm_softc *, int);
+void w83627ehf_refresh_temptarget_rw(struct lm_softc *, int);
+void w83627ehf_refresh_temptargettol_rw(struct lm_softc *, int);
+void w83627thf_refresh_fanvolt_rw(struct lm_softc *, int);
 void wb_w83637hf_refresh_vcore(struct lm_softc *, int);
 void wb_refresh_nvolt(struct lm_softc *, int);
 void wb_w83627ehf_refresh_nvolt(struct lm_softc *, int);
 void wb_refresh_temp(struct lm_softc *, int);
 void wb_refresh_fanrpm(struct lm_softc *, int);
 void wb_w83792d_refresh_fanrpm(struct lm_softc *, int);
 
 void as_refresh_temp(struct lm_softc *, int);
@@ -89,17 +101,23 @@ struct lm_sensor lm78_sensors[] = {
        /* Fans */
        { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
        { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
        { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
 
        { NULL }
 };
 
+
 struct lm_sensor w83627hf_sensors[] = {
+       /* The W83627HG only support the manual PWM fan speed control. */
+       { "PWM", SENSOR_PERCENT, 0, 0x5a, w83627hf_refresh_pwm_rw },
+       { "PWM", SENSOR_PERCENT, 0, 0x5b, w83627hf_refresh_pwm_rw },
+       { "PWM 0/1 Clock Freq", SENSOR_INTEGER, 0, 0x5c, wb_refresh_raw_rw },
+
        /* Voltage */
        { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
        { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
        { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
        { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
        { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
        { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
@@ -122,93 +140,342 @@ struct lm_sensor w83627hf_sensors[] = {
 /*
  * The W83627EHF can measure voltages up to 2.048 V instead of the
  * traditional 4.096 V.  For measuring positive voltages, this can be
  * accounted for by halving the resistor factor.  Negative voltages
  * need special treatment, also because the reference voltage is 2.048 V
  * instead of the traditional 3.6 V.
  */
 struct lm_sensor w83627ehf_sensors[] = {
+       /* Controlling parts: Duty Cycle */
+       { "Sys Fan Volt Control", SENSOR_PERCENT, 0, 0x01, 
w83627ehf_refresh_fanvolt_rw },
+       { "CPU Fan Volt Control", SENSOR_PERCENT, 0, 0x03, 
w83627ehf_refresh_fanvolt_rw },
+       { "Aux Fan Volt Control", SENSOR_PERCENT, 0, 0x11, 
w83627ehf_refresh_fanvolt_rw },
+       { "CPU Fan Volt Control", SENSOR_PERCENT, 0, 0x61, 
w83627ehf_refresh_fanvolt_rw },
+       /* Start-up Values */
+       { "Sys Fan Start-up Value", SENSOR_PERCENT, 0, 0x0a, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "CPU Fan Start-up Value", SENSOR_PERCENT, 0, 0x0b, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "Aux Fan Start-up Value", SENSOR_PERCENT, 0, 0x16, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "CPU Fan Start-up Value", SENSOR_PERCENT, 0, 0x65, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       /* Stop Values */
+       { "Sys Fan Stop Value", SENSOR_PERCENT, 0, 0x08, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "CPU Fan Stop Value", SENSOR_PERCENT, 0, 0x09, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "Aux Fan Stop Value", SENSOR_PERCENT, 0, 0x15, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "CPU Fan Stop Value", SENSOR_PERCENT, 0, 0x64, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+
+       /* Controlling parts: PWM/DC */
+       { "Sys Fan Volt Control", SENSOR_INDICATOR, 0, 0x01, 
w83627ehf_refresh_indicator_rw },
+       { "CPU Fan Volt Control", SENSOR_INDICATOR, 0, 0x03, 
w83627ehf_refresh_indicator_rw },
+       { "Aux Fan Volt Control", SENSOR_INDICATOR, 0, 0x11, 
w83627ehf_refresh_indicator_rw },
+       { "CPU Fan Volt Control", SENSOR_INDICATOR, 0, 0x61, 
w83627ehf_refresh_indicator_rw },
+
+#ifdef LMFANCTLRAW
+       /* Smart Fan Configuration Registers, 00h--1Fh */
+       { "0x00: SysFanOut PWM Output Freq", SENSOR_INTEGER, 0, 0x00, 
wb_refresh_raw_rw },
+       { "0x01: SysFanOut", SENSOR_INTEGER, 0, 0x01, wb_refresh_raw_rw },
+       { "0x02: CPUFanOut0 PWM Output Freq", SENSOR_INTEGER, 0, 0x02, 
wb_refresh_raw_rw },
+       { "0x03: CPUFanOut0", SENSOR_INTEGER, 0, 0x03, wb_refresh_raw_rw },
+       { "0x04: Fan Configuration Reg I", SENSOR_INTEGER, 0, 0x04, 
wb_refresh_raw_rw },
+       { "0x05: Sys Target Temp/RPM", SENSOR_INTEGER, 0, 0x05, 
wb_refresh_raw_rw },
+       { "0x06: CPU Target Temp/RPM0", SENSOR_INTEGER, 0, 0x06, 
wb_refresh_raw_rw },
+       { "0x07: Sys/CPU Target Tolerance", SENSOR_INTEGER, 0, 0x07, 
wb_refresh_raw_rw },
+       { "0x08: SysFanOut Stop Value", SENSOR_INTEGER, 0, 0x08, 
wb_refresh_raw_rw },
+       { "0x09: CPUFanOut0 Stop Value", SENSOR_INTEGER, 0, 0x09, 
wb_refresh_raw_rw },
+       { "0x0a: SysFanOut Start-up Value", SENSOR_INTEGER, 0, 0x0a, 
wb_refresh_raw_rw },
+       { "0x0b: CPUFanOut0 Start-up Value", SENSOR_INTEGER, 0, 0x0b, 
wb_refresh_raw_rw },
+       { "0x0c: SysFanOut Stop Time", SENSOR_INTEGER, 0, 0x0c, 
wb_refresh_raw_rw },
+       { "0x0d: CPUFanOut0 Stop Time", SENSOR_INTEGER, 0, 0x0d, 
wb_refresh_raw_rw },
+       { "0x0e: Fan Output Step-down Time", SENSOR_INTEGER, 0, 0x0e, 
wb_refresh_raw_rw },
+       { "0x0f: Fan Output Step-up Time", SENSOR_INTEGER, 0, 0x0f, 
wb_refresh_raw_rw },
+       { "0x10: AuxFanOut PWM Output Freq", SENSOR_INTEGER, 0, 0x10, 
wb_refresh_raw_rw },
+       { "0x11: AuxFanOut", SENSOR_INTEGER, 0, 0x11, wb_refresh_raw_rw },
+       { "0x12: Fan Configuration Reg II", SENSOR_INTEGER, 0, 0x12, 
wb_refresh_raw_rw },
+       { "0x13: Aux Target Temp/RPM", SENSOR_INTEGER, 0, 0x13, 
wb_refresh_raw_rw },
+       { "0x14: Aux Target Tolerance", SENSOR_INTEGER, 0, 0x14, 
wb_refresh_raw_rw },
+       { "0x15: AuxFanOut Stop Value", SENSOR_INTEGER, 0, 0x15, 
wb_refresh_raw_rw },
+       { "0x16: AuxFanOut Start-up Value", SENSOR_INTEGER, 0, 0x16, 
wb_refresh_raw_rw },
+       { "0x17: AuxFanOut Stop Time", SENSOR_INTEGER, 0, 0x17, 
wb_refresh_raw_rw },
+       { "0x18: OVT", SENSOR_INTEGER, 0, 0x18, wb_refresh_raw_rw },
+       { "0x19: reserved", SENSOR_INTEGER, 0, 0x19, wb_refresh_raw_rw },
+       { "0x1a: reserved", SENSOR_INTEGER, 0, 0x1a, wb_refresh_raw_rw },
+       { "0x1b: reserved", SENSOR_INTEGER, 0, 0x1b, wb_refresh_raw_rw },
+       { "0x1c: reserved", SENSOR_INTEGER, 0, 0x1c, wb_refresh_raw_rw },
+       { "0x1d: reserved", SENSOR_INTEGER, 0, 0x1d, wb_refresh_raw_rw },
+       { "0x1e: reserved", SENSOR_INTEGER, 0, 0x1e, wb_refresh_raw_rw },
+       { "0x1f: reserved", SENSOR_INTEGER, 0, 0x1f, wb_refresh_raw_rw },
+
+       { "0x47: Fan Divisor Reg I", SENSOR_INTEGER, 0, 0x47, wb_refresh_raw_rw 
},
+       { "0x4a: CPUFanOut1 Temp Source Select", SENSOR_INTEGER, 0, 0x4a, 
wb_refresh_raw_rw },
+       { "0x4b: Fan Divisor Reg II", SENSOR_INTEGER, 0, 0x4b, 
wb_refresh_raw_rw },
+       { "0x59: Diode Selection", SENSOR_INTEGER, 0, 0x59, wb_refresh_raw_rw },
+       { "0x5d: VBat Monitor Control", SENSOR_INTEGER, 0, 0x5d, 
wb_refresh_raw_rw },
+
+       { "0x60: CPUFanOut1 PWM Output Freq", SENSOR_INTEGER, 0, 0x60, 
wb_refresh_raw_rw },
+       { "0x61: CPUFanOut1", SENSOR_INTEGER, 0, 0x61, wb_refresh_raw_rw },
+       { "0x62: Fan Configuration Reg III", SENSOR_INTEGER, 0, 0x62, 
wb_refresh_raw_rw },
+       { "0x63: CPU Target Temp/RPM1", SENSOR_INTEGER, 0, 0x63, 
wb_refresh_raw_rw },
+       { "0x64: CPUFanOut1 Stop Value", SENSOR_INTEGER, 0, 0x64, 
wb_refresh_raw_rw },
+       { "0x65: CPUFanOut1 Start-up Value", SENSOR_INTEGER, 0, 0x65, 
wb_refresh_raw_rw },
+       { "0x66: CPUFanOut1 Stop Time", SENSOR_INTEGER, 0, 0x66, 
wb_refresh_raw_rw },
+       { "0x67: CPUFanOut0 Max Output Value", SENSOR_INTEGER, 0, 0x67, 
wb_refresh_raw_rw },
+       { "0x68: CPUFanOut0 Output Step Value", SENSOR_INTEGER, 0, 0x68, 
wb_refresh_raw_rw },
+       { "0x69: CPUFanOut1 Max Output Value", SENSOR_INTEGER, 0, 0x69, 
wb_refresh_raw_rw },
+       { "0x6a: CPUFanOut1 Output Step Value", SENSOR_INTEGER, 0, 0x6a, 
wb_refresh_raw_rw },
+#endif /* LMFANCTLRAW */
+
        /* Voltage */
        { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
        { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 
},
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 
},
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 
},
        { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
        { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
        { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
        { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 
2 },
        { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
        { "", SENSOR_VOLTS_DC, 5, 0x52, lm_refresh_volt, RFACT_NONE / 2 },
 
        /* Temperature */
-       { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
-       { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
-       { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+       { "Sys", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+       { "CPU", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+       { "Aux", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+       /* search for Relative Registers in the datasheet for a nice table */
+       /* Controlling parts: Target Temperature */
+       { "Sys Target", SENSOR_TEMP, 0, 0x05, w83627ehf_refresh_temptarget_rw },
+       { "CPU Target", SENSOR_TEMP, 0, 0x06, w83627ehf_refresh_temptarget_rw },
+       { "Aux Target", SENSOR_TEMP, 0, 0x13, w83627ehf_refresh_temptarget_rw },
+       { "CPU Target", SENSOR_TEMP, 0, 0x63, w83627ehf_refresh_temptarget_rw },
+       /* Controlling parts: Target Temperature Tolerance */
+       { "Sys Tolerance", SENSOR_TEMP, 0, 0x05, 
w83627ehf_refresh_temptargettol_rw },
+       { "CPU Tolerance", SENSOR_TEMP, 0, 0x06, 
w83627ehf_refresh_temptargettol_rw },
+       { "Aux Tolerance", SENSOR_TEMP, 0, 0x13, 
w83627ehf_refresh_temptargettol_rw },
+       { "CPU Tolerance", SENSOR_TEMP, 0, 0x63, 
w83627ehf_refresh_temptargettol_rw },
 
        /* Fans */
-       { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+       { "Sys", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+       { "CPU", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+       { "Aux", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+/*     { "CPU", SENSOR_FANRPM, 0, 0x3f, w83627ehf_refresh_fanrpm },
+       { "Aux", SENSOR_FANRPM, 0, 0x53, w83627ehf_refresh_fanrpm },*/
 
        { NULL }
 };
 
 /* 
  * w83627dhg is almost identical to w83627ehf, except that 
  * it has 9 instead of 10 voltage sensors
  */
 struct lm_sensor w83627dhg_sensors[] = {
+       /* Controlling parts: Duty Cycle */
+       { "Sys Fan Volt Control", SENSOR_PERCENT, 0, 0x01, 
w83627ehf_refresh_fanvolt_rw },
+       { "CPU Fan Volt Control", SENSOR_PERCENT, 0, 0x03, 
w83627ehf_refresh_fanvolt_rw },
+       { "Aux Fan Volt Control", SENSOR_PERCENT, 0, 0x11, 
w83627ehf_refresh_fanvolt_rw },
+       { "CPU Fan Volt Control", SENSOR_PERCENT, 0, 0x61, 
w83627ehf_refresh_fanvolt_rw },
+       /* Start-up Values */
+       { "Sys Fan Start-up Value", SENSOR_PERCENT, 0, 0x0a, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "CPU Fan Start-up Value", SENSOR_PERCENT, 0, 0x0b, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "Aux Fan Start-up Value", SENSOR_PERCENT, 0, 0x16, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "CPU Fan Start-up Value", SENSOR_PERCENT, 0, 0x65, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       /* Stop Values */
+       { "Sys Fan Stop Value", SENSOR_PERCENT, 0, 0x08, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "CPU Fan Stop Value", SENSOR_PERCENT, 0, 0x09, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "Aux Fan Stop Value", SENSOR_PERCENT, 0, 0x15, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "CPU Fan Stop Value", SENSOR_PERCENT, 0, 0x64, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+
+       /* Controlling parts: PWM/DC */
+       { "Sys Fan Volt Control", SENSOR_INDICATOR, 0, 0x01, 
w83627ehf_refresh_indicator_rw },
+       { "CPU Fan Volt Control", SENSOR_INDICATOR, 0, 0x03, 
w83627ehf_refresh_indicator_rw },
+       { "Aux Fan Volt Control", SENSOR_INDICATOR, 0, 0x11, 
w83627ehf_refresh_indicator_rw },
+       { "CPU Fan Volt Control", SENSOR_INDICATOR, 0, 0x61, 
w83627ehf_refresh_indicator_rw },
+
+#ifdef LMFANCTLRAW
+       /* Smart Fan Configuration Registers, 00h--1Fh */
+       { "0x00: SysFanOut PWM Output Freq", SENSOR_INTEGER, 0, 0x00, 
wb_refresh_raw_rw },
+       { "0x01: SysFanOut", SENSOR_INTEGER, 0, 0x01, wb_refresh_raw_rw },
+       { "0x02: CPUFanOut0 PWM Output Freq", SENSOR_INTEGER, 0, 0x02, 
wb_refresh_raw_rw },
+       { "0x03: CPUFanOut0", SENSOR_INTEGER, 0, 0x03, wb_refresh_raw_rw },
+       { "0x04: Fan Configuration Reg I", SENSOR_INTEGER, 0, 0x04, 
wb_refresh_raw_rw },
+       { "0x05: Sys Target Temp/RPM", SENSOR_INTEGER, 0, 0x05, 
wb_refresh_raw_rw },
+       { "0x06: CPU Target Temp/RPM0", SENSOR_INTEGER, 0, 0x06, 
wb_refresh_raw_rw },
+       { "0x07: Sys/CPU Target Tolerance", SENSOR_INTEGER, 0, 0x07, 
wb_refresh_raw_rw },
+       { "0x08: SysFanOut Stop Value", SENSOR_INTEGER, 0, 0x08, 
wb_refresh_raw_rw },
+       { "0x09: CPUFanOut0 Stop Value", SENSOR_INTEGER, 0, 0x09, 
wb_refresh_raw_rw },
+       { "0x0a: SysFanOut Start-up Value", SENSOR_INTEGER, 0, 0x0a, 
wb_refresh_raw_rw },
+       { "0x0b: CPUFanOut0 Start-up Value", SENSOR_INTEGER, 0, 0x0b, 
wb_refresh_raw_rw },
+       { "0x0c: SysFanOut Stop Time", SENSOR_INTEGER, 0, 0x0c, 
wb_refresh_raw_rw },
+       { "0x0d: CPUFanOut0 Stop Time", SENSOR_INTEGER, 0, 0x0d, 
wb_refresh_raw_rw },
+       { "0x0e: Fan Output Step-down Time", SENSOR_INTEGER, 0, 0x0e, 
wb_refresh_raw_rw },
+       { "0x0f: Fan Output Step-up Time", SENSOR_INTEGER, 0, 0x0f, 
wb_refresh_raw_rw },
+       { "0x10: AuxFanOut PWM Output Freq", SENSOR_INTEGER, 0, 0x10, 
wb_refresh_raw_rw },
+       { "0x11: AuxFanOut", SENSOR_INTEGER, 0, 0x11, wb_refresh_raw_rw },
+       { "0x12: Fan Configuration Reg II", SENSOR_INTEGER, 0, 0x12, 
wb_refresh_raw_rw },
+       { "0x13: Aux Target Temp/RPM", SENSOR_INTEGER, 0, 0x13, 
wb_refresh_raw_rw },
+       { "0x14: Aux Target Tolerance", SENSOR_INTEGER, 0, 0x14, 
wb_refresh_raw_rw },
+       { "0x15: AuxFanOut Stop Value", SENSOR_INTEGER, 0, 0x15, 
wb_refresh_raw_rw },
+       { "0x16: AuxFanOut Start-up Value", SENSOR_INTEGER, 0, 0x16, 
wb_refresh_raw_rw },
+       { "0x17: AuxFanOut Stop Time", SENSOR_INTEGER, 0, 0x17, 
wb_refresh_raw_rw },
+       { "0x18: OVT", SENSOR_INTEGER, 0, 0x18, wb_refresh_raw_rw },
+       { "0x19: reserved", SENSOR_INTEGER, 0, 0x19, wb_refresh_raw_rw },
+       { "0x1a: reserved", SENSOR_INTEGER, 0, 0x1a, wb_refresh_raw_rw },
+       { "0x1b: reserved", SENSOR_INTEGER, 0, 0x1b, wb_refresh_raw_rw },
+       { "0x1c: reserved", SENSOR_INTEGER, 0, 0x1c, wb_refresh_raw_rw },
+       { "0x1d: reserved", SENSOR_INTEGER, 0, 0x1d, wb_refresh_raw_rw },
+       { "0x1e: reserved", SENSOR_INTEGER, 0, 0x1e, wb_refresh_raw_rw },
+       { "0x1f: reserved", SENSOR_INTEGER, 0, 0x1f, wb_refresh_raw_rw },
+
+       { "0x47: Fan Divisor Reg I", SENSOR_INTEGER, 0, 0x47, wb_refresh_raw_rw 
},
+       { "0x49: CPUFanOut0/Aux Temp Source Select", SENSOR_INTEGER, 0, 0x49, 
wb_refresh_raw_rw }, /*dhg only, not on ehf*/
+       { "0x4a: CPUFanOut1 Temp Source Select", SENSOR_INTEGER, 0, 0x4a, 
wb_refresh_raw_rw },
+       { "0x4b: Fan Divisor Reg II", SENSOR_INTEGER, 0, 0x4b, 
wb_refresh_raw_rw },
+       { "0x59: Diode Selection", SENSOR_INTEGER, 0, 0x59, wb_refresh_raw_rw },
+       { "0x5d: VBat Monitor Control", SENSOR_INTEGER, 0, 0x5d, 
wb_refresh_raw_rw },
+
+       { "0x60: CPUFanOut1 PWM Output Freq", SENSOR_INTEGER, 0, 0x60, 
wb_refresh_raw_rw },
+       { "0x61: CPUFanOut1", SENSOR_INTEGER, 0, 0x61, wb_refresh_raw_rw },
+       { "0x62: Fan Configuration Reg III", SENSOR_INTEGER, 0, 0x62, 
wb_refresh_raw_rw },
+       { "0x63: CPU Target Temp/RPM1", SENSOR_INTEGER, 0, 0x63, 
wb_refresh_raw_rw },
+       { "0x64: CPUFanOut1 Stop Value", SENSOR_INTEGER, 0, 0x64, 
wb_refresh_raw_rw },
+       { "0x65: CPUFanOut1 Start-up Value", SENSOR_INTEGER, 0, 0x65, 
wb_refresh_raw_rw },
+       { "0x66: CPUFanOut1 Stop Time", SENSOR_INTEGER, 0, 0x66, 
wb_refresh_raw_rw },
+       { "0x67: CPUFanOut0 Max Output Value", SENSOR_INTEGER, 0, 0x67, 
wb_refresh_raw_rw },
+       { "0x68: CPUFanOut0 Output Step Value", SENSOR_INTEGER, 0, 0x68, 
wb_refresh_raw_rw },
+       { "0x69: CPUFanOut1 Max Output Value", SENSOR_INTEGER, 0, 0x69, 
wb_refresh_raw_rw },
+       { "0x6a: CPUFanOut1 Output Step Value", SENSOR_INTEGER, 0, 0x6a, 
wb_refresh_raw_rw },
+#endif /* LMFANCTLRAW */
+
        /* Voltage */
        { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
        { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 
},
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 
},
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 
},
        { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
        { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
        { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
        { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 
2 },
        { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
 
        /* Temperature */
-       { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
-       { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
-       { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+       { "Sys", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+       { "CPU", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+       { "Aux", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+       /* search for Relative Registers in the datasheet for a nice table */
+       /* Controlling parts: Target Temperature */
+       { "Sys Target", SENSOR_TEMP, 0, 0x05, w83627ehf_refresh_temptarget_rw },
+       { "CPU Target", SENSOR_TEMP, 0, 0x06, w83627ehf_refresh_temptarget_rw },
+       { "Aux Target", SENSOR_TEMP, 0, 0x13, w83627ehf_refresh_temptarget_rw },
+       { "CPU Target", SENSOR_TEMP, 0, 0x63, w83627ehf_refresh_temptarget_rw },
+       /* Controlling parts: Target Temperature Tolerance */
+       { "Sys Tolerance", SENSOR_TEMP, 0, 0x05, 
w83627ehf_refresh_temptargettol_rw },
+       { "CPU Tolerance", SENSOR_TEMP, 0, 0x06, 
w83627ehf_refresh_temptargettol_rw },
+       { "Aux Tolerance", SENSOR_TEMP, 0, 0x13, 
w83627ehf_refresh_temptargettol_rw },
+       { "CPU Tolerance", SENSOR_TEMP, 0, 0x63, 
w83627ehf_refresh_temptargettol_rw },
 
        /* Fans */
-       { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+       { "Sys", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+       { "CPU", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+       { "Aux", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+/*     { "CPU", SENSOR_FANRPM, 0, 0x3f, w83627ehf_refresh_fanrpm },*/
 
        { NULL }
 };
 
-struct lm_sensor w83637hf_sensors[] = {
+struct lm_sensor w83627thf_sensors[] = {
+       /* Controlling parts: Duty Cycle */
+       { "Sys Fan Volt Control", SENSOR_PERCENT, 0, 0x01, 
w83627thf_refresh_fanvolt_rw },
+       { "CPU Fan Volt Control", SENSOR_PERCENT, 0, 0x03, 
w83627thf_refresh_fanvolt_rw },
+       { "Aux Fan Volt Control", SENSOR_PERCENT, 0, 0x11, 
w83627thf_refresh_fanvolt_rw },
+       /* Start-up Values */
+       { "Sys Fan Start-up Value", SENSOR_PERCENT, 0, 0x0a, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "CPU Fan Start-up Value", SENSOR_PERCENT, 0, 0x0b, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       { "Aux Fan Start-up Value", SENSOR_PERCENT, 0, 0x16, 
w83627ehf_refresh_fanvolt_thermal_rw, 0 },
+       /* Stop Values */
+       { "Sys Fan Stop Value", SENSOR_PERCENT, 0, 0x08, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "CPU Fan Stop Value", SENSOR_PERCENT, 0, 0x09, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+       { "Aux Fan Stop Value", SENSOR_PERCENT, 0, 0x15, 
w83627ehf_refresh_fanvolt_thermal_rw, 1 },
+
+#ifdef LMFANCTLRAW
+       /* Smart Fan Configuration Registers, 00h--1Fh */
+       { "0x00: reserved", SENSOR_INTEGER, 0, 0x00, wb_refresh_raw_rw },
+       { "0x01: SysFanOut", SENSOR_INTEGER, 0, 0x01, wb_refresh_raw_rw },
+       { "0x02: reserved", SENSOR_INTEGER, 0, 0x02, wb_refresh_raw_rw },
+       { "0x03: CPUFanOut", SENSOR_INTEGER, 0, 0x03, wb_refresh_raw_rw },
+       { "0x04: Fan Conguration Reg I", SENSOR_INTEGER, 0, 0x04, 
wb_refresh_raw_rw },
+       { "0x05: Sys Target Temp/RPM", SENSOR_INTEGER, 0, 0x05, 
wb_refresh_raw_rw },
+       { "0x06: CPU Target Temp/RPM", SENSOR_INTEGER, 0, 0x06, 
wb_refresh_raw_rw },
+       { "0x07: Sys/CPU Target Tolerance", SENSOR_INTEGER, 0, 0x07, 
wb_refresh_raw_rw },
+       { "0x08: SysFanOut Stop Value", SENSOR_INTEGER, 0, 0x08, 
wb_refresh_raw_rw },
+       { "0x09: CPUFanOut Stop Value", SENSOR_INTEGER, 0, 0x09, 
wb_refresh_raw_rw },
+       { "0x0a: SysFanOut Start-up Value", SENSOR_INTEGER, 0, 0x0a, 
wb_refresh_raw_rw },
+       { "0x0b: CPUFanOut Start-up Value", SENSOR_INTEGER, 0, 0x0b, 
wb_refresh_raw_rw },
+       { "0x0c: SysFanOut Stop Time", SENSOR_INTEGER, 0, 0x0c, 
wb_refresh_raw_rw },
+       { "0x0d: CPUFanOut Stop Time", SENSOR_INTEGER, 0, 0x0d, 
wb_refresh_raw_rw },
+       { "0x0e: Fan Output Step-down Time", SENSOR_INTEGER, 0, 0x0e, 
wb_refresh_raw_rw },
+       { "0x0f: Fan Output Step-up Time", SENSOR_INTEGER, 0, 0x0f, 
wb_refresh_raw_rw },
+       { "0x10: reserved", SENSOR_INTEGER, 0, 0x10, wb_refresh_raw_rw },
+       { "0x11: AuxFanOut", SENSOR_INTEGER, 0, 0x11, wb_refresh_raw_rw },
+       { "0x12: Fan Configuration Reg II", SENSOR_INTEGER, 0, 0x12, 
wb_refresh_raw_rw },
+       { "0x13: Aux Target Temp/RPM", SENSOR_INTEGER, 0, 0x13, 
wb_refresh_raw_rw },
+       { "0x14: Aux Target Tolerance", SENSOR_INTEGER, 0, 0x14, 
wb_refresh_raw_rw },
+       { "0x15: AuxFanOut Stop Value", SENSOR_INTEGER, 0, 0x15, 
wb_refresh_raw_rw },
+       { "0x16: AuxFanOut Start-up Value", SENSOR_INTEGER, 0, 0x16, 
wb_refresh_raw_rw },
+       { "0x17: AuxFanOut Stop Time", SENSOR_INTEGER, 0, 0x17, 
wb_refresh_raw_rw },
+       { "0x18: VRM and OVT", SENSOR_INTEGER, 0, 0x18, wb_refresh_raw_rw },
+       { "0x19: reserved", SENSOR_INTEGER, 0, 0x19, wb_refresh_raw_rw },
+       { "0x1a: user-defined", SENSOR_INTEGER, 0, 0x1a, wb_refresh_raw_rw },
+       { "0x1b: user-defined", SENSOR_INTEGER, 0, 0x1b, wb_refresh_raw_rw },
+       { "0x1c: reserved", SENSOR_INTEGER, 0, 0x1c, wb_refresh_raw_rw },
+       { "0x1d: reserved", SENSOR_INTEGER, 0, 0x1d, wb_refresh_raw_rw },
+       { "0x1e: reserved", SENSOR_INTEGER, 0, 0x1e, wb_refresh_raw_rw },
+       { "0x1f: reserved", SENSOR_INTEGER, 0, 0x1f, wb_refresh_raw_rw },
+
+       { "0x47: Fan Divisor Reg I", SENSOR_INTEGER, 0, 0x47, wb_refresh_raw_rw 
},
+#endif /* LMFANCTLRAW */
+
        /* Voltage */
        { "VCore", SENSOR_VOLTS_DC, 0, 0x20, wb_w83637hf_refresh_vcore },
        { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(28, 10) },
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
        { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 51) },
        { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_refresh_nvolt, RFACT(232, 56) },
        { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 51) },
        { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
 
        /* Temperature */
-       { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
-       { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
-       { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+       { "Sys", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+       { "CPU", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+       { "Aux", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+       /* Controlling parts: Target Temperature */
+       { "Sys Target", SENSOR_TEMP, 0, 0x05, w83627ehf_refresh_temptarget_rw },
+       { "CPU Target", SENSOR_TEMP, 0, 0x06, w83627ehf_refresh_temptarget_rw },
+       { "Aux Target", SENSOR_TEMP, 0, 0x13, w83627ehf_refresh_temptarget_rw },
+       /* Controlling parts: Target Temperature Tolerance */
+       { "Sys Tolerance", SENSOR_TEMP, 0, 0x05, 
w83627ehf_refresh_temptargettol_rw },
+       { "CPU Tolerance", SENSOR_TEMP, 0, 0x06, 
w83627ehf_refresh_temptargettol_rw },
+       { "Aux Tolerance", SENSOR_TEMP, 0, 0x13, 
w83627ehf_refresh_temptargettol_rw },
 
        /* Fans */
-       { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
-       { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+       { "Sys", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+       { "CPU", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+       { "Aux", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
 
        { NULL }
 };
 
+/*
+ * With regard to fan control, W83697HF documentation appears to be
+ * contradictory, as section 6.4.2 "Fan speed control" suggests
+ * using CR5A and CR5B, like in W83627HG, for PWM duty cycle,
+ * whereas other sections define these registers differently,
+ * and suggest registers similar to the other chips that implement
+ * the Smart Fan control system.
+ * Support for fan-controlling with this chip might be added later.
+ */
 struct lm_sensor w83697hf_sensors[] = {
        /* Voltage */
        { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
        { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
        { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
        { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
        { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
        { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
@@ -394,17 +661,17 @@ void
 lm_attach(struct lm_softc *sc)
 {
        u_int i, config;
 
        /* No point in doing anything if we don't have any sensors. */
        if (sc->numsensors == 0)
                return;
 
-       if (sensor_task_register(sc, lm_refresh, 5)) {
+       if (sensor_task_register(sc, lm_refresh, 3)) {
                device_printf(sc->sc_dev, "unable to register update task\n");
                return;
        }
 
        /* Start the monitoring loop */
        config = sc->lm_readreg(sc, LM_CONFIG);
        sc->lm_writereg(sc, LM_CONFIG, config | 0x01);
 
@@ -508,33 +775,37 @@ wb_match(struct lm_softc *sc)
        DPRINTF((" winbond chip id 0x%x\n", sc->chipid));
        switch(sc->chipid) {
        case WB_CHIPID_W83627HF:
                cdesc = "W83627HF";
                lm_setup_sensors(sc, w83627hf_sensors);
                break;
        case WB_CHIPID_W83627THF:
                cdesc = "W83627THF";
-               lm_setup_sensors(sc, w83637hf_sensors);
+               sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+               if (sc->lm_readreg(sc, WB_BANK0_CONFIG) & WB_CONFIG_VMR9)
+                       sc->vrm9 = 1;
+               sc->lm_writereg(sc, WB_BANKSEL, banksel);
+               lm_setup_sensors(sc, w83627thf_sensors);
                break;
        case WB_CHIPID_W83627EHF:
                cdesc = "W83627EHF";
                lm_setup_sensors(sc, w83627ehf_sensors);
                break;
        case WB_CHIPID_W83627DHG:
                cdesc = "W83627DHG";
                lm_setup_sensors(sc, w83627dhg_sensors);
                break;
        case WB_CHIPID_W83637HF:
                cdesc = "W83637HF";
                sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
                if (sc->lm_readreg(sc, WB_BANK0_CONFIG) & WB_CONFIG_VMR9)
                        sc->vrm9 = 1;
                sc->lm_writereg(sc, WB_BANKSEL, banksel);
-               lm_setup_sensors(sc, w83637hf_sensors);
+               lm_setup_sensors(sc, w83627thf_sensors);
                break;
        case WB_CHIPID_W83697HF:
                cdesc = "W83697HF";
                lm_setup_sensors(sc, w83697hf_sensors);
                break;
        case WB_CHIPID_W83781D:
        case WB_CHIPID_W83781D_2:
                cdesc = "W83781D";
@@ -601,16 +872,22 @@ void
 lm_setup_sensors(struct lm_softc *sc, struct lm_sensor *sensors)
 {
        int i;
 
        for (i = 0; sensors[i].desc; i++) {
                sc->sensors[i].type = sensors[i].type;
                strlcpy(sc->sensors[i].desc, sensors[i].desc,
                    sizeof(sc->sensors[i].desc));
+               if (sc->sensors[i].type == SENSOR_PERCENT ||
+                   sc->sensors[i].type == SENSOR_INDICATOR ||
+                   sc->sensors[i].type == SENSOR_INTEGER ||
+                   (sc->sensors[i].type == SENSOR_TEMP &&
+                   sc->sensors[i].desc[4] != '\0'))
+                       sc->sensors[i].flags = SENSOR_FCONTROLLABLE;
                sc->numsensors++;
        }
        sc->lm_sensors = sensors;
 }
 
 void
 lm_refresh(void *arg)
 {
@@ -710,16 +987,446 @@ wb_refresh_sensor_data(struct lm_softc *sc)
                        sc->lm_writereg(sc, WB_BANKSEL, bank);
                }
                sc->lm_sensors[i].refresh(sc, i);
        }
        sc->lm_writereg(sc, WB_BANKSEL, banksel);
 }
 
 void
+wb_refresh_raw_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (nv >= 0x00 && nv <= 0xff)
+                       sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+       }
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       sensor->value = data;
+}
+
+void
+w83627hf_refresh_pwm_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (nv < 0 || nv > 100)
+                       nv = 100;       /* ramp it up! */
+               nv = 255 * nv / 100;
+               sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+       }
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       /* 1000 is the factor to convert from percentage to SENSOR_PERCENT */
+       sensor->value = 1000 * 100 * data / 255;
+}
+
+void
+w83627ehf_refresh_fanvolt_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+       int confreg, selshift, modeshift, conf, sel, mode;
+       const char* const sels[2] =
+           { "PWM", "DC" };
+       const char* const modes[4] =
+           { "Manual", "Thermal", "Speed", "SmartIII" };
+
+       /* get the right Fan Configuration Register */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x01:      /* Sys */
+               confreg = 0x04; selshift = 0; modeshift = 2;
+               break;
+       case 0x03:      /* CPU */
+               confreg = 0x04; selshift = 1; modeshift = 4;
+               break;
+       case 0x11:      /* Aux */
+               confreg = 0x12; selshift = 0; modeshift = 1;
+               break;
+       case 0x61:      /* CPU */
+               confreg = 0x5b; selshift = 6; modeshift = 4;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (nv < 0 || nv > 100)
+                       nv = 100;       /* ramp it up! */
+               nv = 255 * nv / 100;
+               /*
+                * Since we got an explicit request to affect the speed,
+                * we need to go into manual mode first.
+                */
+               conf = sc->lm_readreg(sc, confreg);
+               mode = (conf >> modeshift) & 0x3;
+               if (mode != 0) {
+                       conf &= ~(0x3 << modeshift);
+                       sc->lm_writereg(sc, confreg, conf);
+               }
+               sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+       }
+
+       /* now populate our struct ksensor */
+
+       conf = sc->lm_readreg(sc, confreg);
+       sel = (conf >> selshift) & 0x1;
+       mode = (conf >> modeshift) & 0x3;
+       ksnprintf(sensor->desc + 8, sizeof(sensor->desc) - 8,
+           "%s %s", sels[sel], modes[mode]);
+
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       /* 1000 is the factor to convert from percentage to SENSOR_PERCENT */
+       sensor->value = 1000 * 100 * data / 255;
+#ifdef LMFANDUTYHINTS
+       if (data > 148)
+               sensor->status = SENSOR_S_OK;   /* at least 7V */
+       else if (data > 106)
+               sensor->status = SENSOR_S_WARN; /* at least 5V */
+       else
+               sensor->status = SENSOR_S_CRIT; /* less than 5V */
+#endif /* LMFANDUTYHINTS */
+}
+
+/*
+ * Fan Start-up and Stop Values are only used in Thermal Cruise mode.
+ * The documentation is ambiguous of whether they are used in other
+ * modes, too.
+ */
+void
+w83627ehf_refresh_fanvolt_thermal_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+       int confreg, selshift, modeshift, conf, mode, minfanbit, minfan;
+
+       /*
+        * Get the right Fan Configuration Register.
+        * Register 0x12 has the "Keep Min. Fan Output Value" bools indicating
+        * whether the output should or should not go below the stop value.
+        */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x0a:
+       case 0x08:
+               /* Sys */
+               confreg = 0x04; selshift = 0; modeshift = 2;
+               minfanbit = 5;
+               break;
+       case 0x0b:
+       case 0x09:
+               /* CPU */
+               confreg = 0x04; selshift = 1; modeshift = 4;
+               minfanbit = 4;
+               break;
+       case 0x16:
+       case 0x15:
+               /* Aux */
+               confreg = 0x12; selshift = 0; modeshift = 1;
+               minfanbit = 3;
+               break;
+       case 0x65:
+       case 0x64:
+               /* CPU */
+               confreg = 0x5b; selshift = 6; modeshift = 4;
+               minfanbit = 6;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (!(nv < 0 || nv > 100)) {
+                       if (sc->lm_sensors[n].rfact == 1) {
+                               conf = sc->lm_readreg(sc, 0x12);
+                               minfan = (conf >> minfanbit) & 0x1;
+                               if (minfan != 1) {
+                                       conf |= (0x1 << minfanbit);
+                                       sc->lm_writereg(sc, 0x12, conf);
+                               }
+                       }
+                       nv = 255 * nv / 100;
+                       sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+               }
+       }
+
+       /* now populate our struct ksensor */
+
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       /* 1000 is the factor to convert from percentage to SENSOR_PERCENT */
+       sensor->value = 1000 * 100 * data / 255;
+
+       conf = sc->lm_readreg(sc, confreg);
+       mode = (conf >> modeshift) & 0x3;
+       if (sc->lm_sensors[n].rfact == 1) {
+               conf = sc->lm_readreg(sc, 0x12);
+               minfan = (conf >> minfanbit) & 0x1;
+       } else
+               minfan = 1;
+
+       if (mode == 0x1 && minfan == 1)
+               sensor->flags &= ~SENSOR_FUNKNOWN;
+       else
+               sensor->flags |= SENSOR_FUNKNOWN;
+
+#ifdef LMFANDUTYHINTS
+       if (sensor->flags & SENSOR_FUNKNOWN)
+               sensor->status = SENSOR_S_UNSPEC;
+       else if (data > 148)
+               sensor->status = SENSOR_S_OK;   /* at least 7V */
+       else if (data > 106)
+               sensor->status = SENSOR_S_WARN; /* at least 5V */
+       else
+               sensor->status = SENSOR_S_CRIT; /* less than 5V */
+#endif /* LMFANDUTYHINTS */
+}
+
+void
+w83627ehf_refresh_indicator_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv;
+       int confreg, selshift, modeshift, conf, sel;
+       const char* const sels[2] =
+           { "PWM", "DC" };
+
+       /* get the right Fan Configuration Register */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x01:      /* Sys */
+               confreg = 0x04; selshift = 0; modeshift = 2;
+               break;
+       case 0x03:      /* CPU */
+               confreg = 0x04; selshift = 1; modeshift = 4;
+               break;
+       case 0x11:      /* Aux */
+               confreg = 0x12; selshift = 0; modeshift = 1;
+               break;
+       case 0x61:      /* CPU */
+               confreg = 0x5b; selshift = 6; modeshift = 4;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (nv == 0 || nv == 1) {
+                       conf = sc->lm_readreg(sc, confreg);
+                       sel = (conf >> selshift) & 0x1;
+                       if (sel != nv) {
+                               if (nv == 0)
+                                       conf &= ~(0x1 << selshift);
+                               else
+                                       conf |= (0x1 << selshift);
+                               sc->lm_writereg(sc, confreg, conf);
+                       }
+               }
+       }
+
+       conf = sc->lm_readreg(sc, confreg);
+       sel = (conf >> selshift) & 0x1;
+       sensor->value = sel;
+       ksnprintf(sensor->desc + 8, sizeof(sensor->desc) - 8,
+           "%s/%s: %s", sels[0], sels[1], sels[sel]);
+}
+
+void
+w83627ehf_refresh_temptarget_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+       int confreg, modeshift, tolreg, tolsh, conf, mode;
+
+       /* get the right Fan Configuration Register and Tolerance */
+       /* search for Relative Registers in the datasheet for a nice table */
+       /* .reg in this switch is the target cruise register */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x05:      /* Sys */
+               confreg = 0x04; modeshift = 2; tolreg = 0x07; tolsh = 0;
+               break;
+       case 0x06:      /* CPU */
+               confreg = 0x04; modeshift = 4; tolreg = 0x07; tolsh = 4;
+               break;
+       case 0x13:      /* Aux */
+               confreg = 0x12; modeshift = 1; tolreg = 0x14; tolsh = 0;
+               break;
+       case 0x63:      /* CPU */
+               confreg = 0x5b; modeshift = 4; tolreg = 0x62; tolsh = 0;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               /*
+                * If the new value is invalid, assume 'panic',
+                * and make 36 degC the new target temp.
+                */
+               if (nv < 0 || nv > 127)
+                       nv = 36;
+               /*
+                * Since we got an explicit request to affect the speed,
+                * we need to go into Temperature Cruise mode first.
+                */
+               conf = sc->lm_readreg(sc, confreg);
+               mode = (conf >> modeshift) & 0x3;
+               if (mode != 0x1) {
+                       conf &= ~(0x3 << modeshift);
+                       conf |= (0x1 << modeshift);
+                       sc->lm_writereg(sc, confreg, conf);
+               }
+               sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+       }
+
+       /* now populate our struct ksensor */
+
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       sensor->value = data * 1000000 + 273150000;
+
+       conf = sc->lm_readreg(sc, confreg);
+       mode = (conf >> modeshift) & 0x3;
+       if (mode != 0x1)
+               sensor->flags |= SENSOR_FUNKNOWN;
+       else
+               sensor->flags &= ~SENSOR_FUNKNOWN;
+}
+
+void
+w83627ehf_refresh_temptargettol_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+       int confreg, modeshift, tolreg, tolsh, conf, mode;
+
+       /* get the right Fan Configuration Register and Tolerance */
+       /* search for Relative Registers in the datasheet for a nice table */
+       /* .reg in this switch is the target cruise register */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x05:      /* Sys */
+               confreg = 0x04; modeshift = 2; tolreg = 0x07; tolsh = 0;
+               break;
+       case 0x06:      /* CPU */
+               confreg = 0x04; modeshift = 4; tolreg = 0x07; tolsh = 4;
+               break;
+       case 0x13:      /* Aux */
+               confreg = 0x12; modeshift = 1; tolreg = 0x14; tolsh = 0;
+               break;
+       case 0x63:      /* CPU */
+               confreg = 0x5b; modeshift = 4; tolreg = 0x62; tolsh = 0;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (!(nv < 0 || nv > 16)) {
+                       data = sc->lm_readreg(sc, tolreg);
+                       data &= ~(0xf << tolsh);
+                       data |= (nv << tolsh);
+                       sc->lm_writereg(sc, tolreg, data);
+               }
+       }
+
+       /* now populate our struct ksensor */
+
+       data = sc->lm_readreg(sc, tolreg);
+       data &= (0xf << tolsh);
+       data >>= tolsh;
+       sensor->value = data * 1000000 + 273150000;
+
+       conf = sc->lm_readreg(sc, confreg);
+       mode = (conf >> modeshift) & 0x3;
+       if (mode != 0x1)
+               sensor->flags |= SENSOR_FUNKNOWN;
+       else
+               sensor->flags &= ~SENSOR_FUNKNOWN;
+}
+
+void
+w83627thf_refresh_fanvolt_rw(struct lm_softc *sc, int n)
+{
+       struct ksensor *sensor = &sc->sensors[n];
+       int nv, data;
+       int confreg, modeshift, conf, mode;
+       const char* const modes[4] =
+           { "Manual", "Thermal", "Speed", "" };
+
+       /* get the right Fan Configuration Register */
+       switch (sc->lm_sensors[n].reg) {
+       case 0x01:      /* Sys */
+               confreg = 0x04; modeshift = 2;
+               break;
+       case 0x03:      /* CPU */
+               confreg = 0x04; modeshift = 4;
+               break;
+       case 0x11:      /* Aux */
+               confreg = 0x12; modeshift = 1;
+               break;
+       default:
+               return;
+       }
+
+       if (sensor->flags & SENSOR_FNEWVALUE) {
+               sensor->flags &= ~SENSOR_FNEWVALUE;
+               nv = sensor->upvalue;
+               if (nv < 0 || nv > 100)
+                       nv = 100;       /* ramp it up! */
+               /* only 4 out of 8 bits are used to control voltage */
+               nv = 0xf * nv / 100;
+               nv <<= 4;
+               /*
+                * Since we got an explicit request to affect the speed,
+                * we need to go into manual mode first.
+                */
+               conf = sc->lm_readreg(sc, confreg);
+               mode = (conf >> modeshift) & 0x3;
+               if (mode != 0) {
+                       conf &= ~(0x3 << modeshift);
+                       sc->lm_writereg(sc, confreg, conf);
+               }
+               sc->lm_writereg(sc, sc->lm_sensors[n].reg, nv);
+       }
+
+       /* now populate our struct ksensor */
+
+       conf = sc->lm_readreg(sc, confreg);
+       mode = (conf >> modeshift) & 0x3;
+       ksnprintf(sensor->desc + 8, sizeof(sensor->desc) - 8,
+           "Ctl %s", modes[mode]);
+
+       data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+       data >>= 4;
+       /* 1000 is the factor to convert from percentage to SENSOR_PERCENT */
+       sensor->value = 1000 * 100 * data / 0xf;
+#ifdef LMFANDUTYHINTS
+       if (data > 0x8)
+               sensor->status = SENSOR_S_OK;   /* at least 7.2V */
+       else if (data > 0x5)
+               sensor->status = SENSOR_S_WARN; /* at least 4.8V */
+       else
+               sensor->status = SENSOR_S_CRIT; /* 4.0V and less */
+#endif /* LMFANDUTYHINTS */
+}
+
+void
 wb_w83637hf_refresh_vcore(struct lm_softc *sc, int n)
 {
        struct ksensor *sensor = &sc->sensors[n];
        int data;
 
        data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
 
        /*
-- 
1.6.6

Reply via email to