[PATCH] fanctl: AsiaBSDCon 2010 DragonFly sysctl hw.sensors lm(4) fan control similar to BSDCan 2009 OpenBSD

Constantine A. Murenin cnst+dfly at bugmail.mojo.ru
Fri Mar 12 00:21:14 PST 2010


* This is a ported version of the sysctl hw.sensors lm(4)
  fan-controlling prototype/hack as posted to the tech at 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]);
+
+	/* 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


--opJtzjQTFsWo+cga--





More information about the Kernel mailing list