From - Fri May 08 18:01:12 2009 Date: Fri, 8 May 2009 17:59:06 -0400 From: "Constantine A. Murenin" To: tech@openbsd.org Cc: "Constantine A. Murenin" Subject: sysctl hw.sensors lm(4) fan-controlling prototype/hack Message-ID: <20090508215906.GA32513@core.home.const.name> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.4.2.3i Organisation: OpenBSD Dear tech@, It is my great pleasure to announce a patch that allows interested users to monitor and control the speed of the fans connected to some popular Winbond Super I/O Hardware Monitors. The patch below supports the following chips, as shown by their 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. I will thus describe the functionality of the latter family, W83627EHF and W83627DHG, some parts of which will not be applicable to 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 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. *** WARNING: *** The patch doesn't make any unconditional modifications to the behaviour of the chip, unless you specifically alter one of the modifiable sysctl values. Please, keep in mind, that altering these values is easy, and they affect a major safety harbour of your computer, so it is strongly advised that you ensure you have appropriate watch over the computer with which you are playing before leaving it unattended, if at all. Consider that fans need to run at higher speeds during higher workload, so be specifically careful if playing with the Manual mode. ** NOONE BUT YOU WILL BE RESPONSIBLE IF YOU ABUSE THE PATCH! ** Of course, noone knows if the patch doesn't contain any bugs, or if it is not going to trigger some weird system/bios/acpi bugs and cause a malfunction or a fire, so use at your own risk. I do. :) Anyhow, to make the longer story short, here is the output from a W83627DHG chip on an Intel D201GLY2 box with one small system fan: % dmesg | fgrep W83627DHG wbsio0 at isa0 port 0x4e/2: W83627DHG rev 0x25 lm1 at wbsio0 port 0x290/8: W83627DHG % sysctl hw.sensors hw.sensors.cpu0.temp0=58.00 degC hw.sensors.lm1.temp0=45.00 degC (Sys) hw.sensors.lm1.temp1=51.00 degC (CPU) hw.sensors.lm1.temp2=14.50 degC (Aux) hw.sensors.lm1.temp3=38.00 degC (Sys Target) hw.sensors.lm1.temp4=unknown (CPU Target) hw.sensors.lm1.temp5=unknown (Aux Target) hw.sensors.lm1.temp6=unknown (CPU Target) hw.sensors.lm1.temp7=2.00 degC (Sys Tolerance) hw.sensors.lm1.temp8=unknown (CPU Tolerance) hw.sensors.lm1.temp9=unknown (Aux Tolerance) hw.sensors.lm1.temp10=unknown (CPU Tolerance) hw.sensors.lm1.fan0=1854 RPM (Sys) hw.sensors.lm1.volt0=1.34 VDC (VCore) hw.sensors.lm1.volt1=12.20 VDC (+12V) hw.sensors.lm1.volt2=3.33 VDC (+3.3V) hw.sensors.lm1.volt3=3.33 VDC (+3.3V) hw.sensors.lm1.volt4=-3.95 VDC (-12V) hw.sensors.lm1.volt5=0.11 VDC hw.sensors.lm1.volt6=1.62 VDC hw.sensors.lm1.volt7=3.28 VDC (3.3VSB) hw.sensors.lm1.volt8=0.03 VDC (VBAT) hw.sensors.lm1.indicator0=Off (Sys Fan PWM/DC: PWM) hw.sensors.lm1.indicator1=Off (CPU Fan PWM/DC: PWM) hw.sensors.lm1.indicator2=Off (Aux Fan PWM/DC: PWM) hw.sensors.lm1.indicator3=On (CPU Fan PWM/DC: DC) hw.sensors.lm1.percent0=100.00% (Sys Fan PWM Thermal), OK hw.sensors.lm1.percent1=100.00% (CPU Fan PWM Manual), OK hw.sensors.lm1.percent2=100.00% (Aux Fan PWM Manual), OK hw.sensors.lm1.percent3=100.00% (CPU Fan DC SmartIII), OK hw.sensors.lm1.percent4=0.39% (Sys Fan Start-up Value), CRITICAL hw.sensors.lm1.percent5=unknown (CPU Fan Start-up Value) hw.sensors.lm1.percent6=unknown (Aux Fan Start-up Value) hw.sensors.lm1.percent7=unknown (CPU Fan Start-up Value) hw.sensors.lm1.percent8=29.41% (Sys Fan Stop Value), CRITICAL hw.sensors.lm1.percent9=unknown (CPU Fan Stop Value) hw.sensors.lm1.percent10=unknown (Aux Fan Stop Value) hw.sensors.lm1.percent11=unknown (CPU Fan Stop Value) (Values that are not applicable to the current operational mode are marked as 'unknown' in sysctl.) Here we alter the target temperature value for the Thermal Cruise mode: % sudo sysctl hw.sensors.lm1.temp3=50 hw.sensors.lm1.temp3=38.00 degC {updating} (Sys Target) We now see that the percent0 value went down: % sysctl hw.sensors | fgrep Sys hw.sensors.lm1.temp0=45.00 degC (Sys) hw.sensors.lm1.temp3=50.00 degC (Sys Target) hw.sensors.lm1.temp7=2.00 degC (Sys Tolerance) hw.sensors.lm1.fan0=1739 RPM (Sys) hw.sensors.lm1.indicator0=Off (Sys Fan PWM/DC: PWM) hw.sensors.lm1.percent0=29.41% (Sys Fan PWM Thermal), CRITICAL hw.sensors.lm1.percent4=0.39% (Sys Fan Start-up Value), CRITICAL hw.sensors.lm1.percent8=29.41% (Sys Fan Stop Value), CRITICAL This D201GLY2 board is kinda weird, because the fan doesn't stop much until the duty cycle is almost zero. (Or, perhaps, the issue lies with the fan of the enclosure itself.) So don't try this on other boards; the status field indicates the likelihood that the fan is not going to run on a given voltage. % sudo sysctl hw.sensors.lm1.percent8=10 hw.sensors.lm1.percent8=29.41% {updating} (Sys Fan Stop Value), CRITICAL % sysctl hw.sensors | fgrep Sys hw.sensors.lm1.temp0=45.00 degC (Sys) hw.sensors.lm1.temp3=50.00 degC (Sys Target) hw.sensors.lm1.temp7=2.00 degC (Sys Tolerance) hw.sensors.lm1.fan0=1240 RPM (Sys) hw.sensors.lm1.indicator0=Off (Sys Fan PWM/DC: PWM) hw.sensors.lm1.percent0=9.80% (Sys Fan PWM Thermal), CRITICAL hw.sensors.lm1.percent4=0.39% (Sys Fan Start-up Value), CRITICAL hw.sensors.lm1.percent8=9.80% (Sys Fan Stop Value), CRITICAL Now let's go into the Manual mode: % sudo sysctl hw.sensors.lm1.percent0=6 hw.sensors.lm1.percent0=9.80% {updating} (Sys Fan PWM Thermal), CRITICAL % sysctl hw.sensors | fgrep Sys hw.sensors.lm1.temp0=45.00 degC (Sys) hw.sensors.lm1.temp3=unknown (Sys Target) hw.sensors.lm1.temp7=unknown (Sys Tolerance) hw.sensors.lm1.fan0=1240 RPM (Sys) hw.sensors.lm1.indicator0=Off (Sys Fan PWM/DC: PWM) hw.sensors.lm1.percent0=9.80% (Sys Fan PWM Manual), CRITICAL hw.sensors.lm1.percent4=unknown (Sys Fan Start-up Value) hw.sensors.lm1.percent8=unknown (Sys Fan Stop Value) % sysctl hw.sensors | fgrep Sys hw.sensors.lm1.temp0=45.00 degC (Sys) hw.sensors.lm1.temp3=unknown (Sys Target) hw.sensors.lm1.temp7=unknown (Sys Tolerance) hw.sensors.lm1.fan0=781 RPM (Sys) hw.sensors.lm1.indicator0=Off (Sys Fan PWM/DC: PWM) hw.sensors.lm1.percent0=5.88% (Sys Fan PWM Manual), CRITICAL hw.sensors.lm1.percent4=unknown (Sys Fan Start-up Value) hw.sensors.lm1.percent8=unknown (Sys Fan Stop Value) (Note that the description of the percent0 sensor changed to indicate that the Manual mode is now active, and that the value goes gradually towards the desired value over some period of time.) Note that the driver only implements reading and writing to the registers, e.g. the Thermal Cruise and other modes are still performed by the chip itself. Fan Cruise mode and the Smart Fan III modes are not supported, although you can still monitor their effects via the percent{0,1,2,3} sensors. Etc. Comments and test reports are welcome. If you like this stuff, make sure to come by tomorrow for my BSDCan 2009 talk on Quiet Computing with BSD. :) The talk will, of course, have a bit more details on the subject. Best regards, Constantine.SU. P.S. The patch applies cleanly to OpenBSD 4.3, 4.4, 4.5 and 4.5-current. Index: sys/sys/sensors.h =================================================================== RCS file: /cvs/src/sys/sys/sensors.h,v retrieving revision 1.24 diff -u -d -p -8 -r1.24 sensors.h --- sys/sys/sensors.h 24 Jun 2007 05:34:35 -0000 1.24 +++ sys/sys/sensors.h 8 May 2009 18:47:07 -0000 @@ -96,18 +96,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 */ @@ -124,16 +134,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 */ Index: sys/kern/kern_sysctl.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sysctl.c,v retrieving revision 1.168 diff -u -d -p -8 -r1.168 kern_sysctl.c --- sys/kern/kern_sysctl.c 21 Jan 2009 21:02:40 -0000 1.168 +++ sys/kern/kern_sysctl.c 8 May 2009 18:47:08 -0000 @@ -1825,27 +1825,37 @@ sysctl_sensors(int *name, u_int namelen, type = name[1]; numt = name[2]; ks = sensor_find(dev, type, numt); if (ks == NULL) return (ENOENT); + if (newp && newlen != sizeof(int)) + return (EINVAL); + if (newp && !(ks->flags & SENSOR_FCONTROLLABLE)) + return (EPERM); + if (newp) { + ks->upvalue = *(int*)newp; + ks->flags |= SENSOR_FNEWVALUE; + } + /* Grab a copy, to clear the kernel pointers */ us = malloc(sizeof(*us), M_TEMP, M_WAITOK|M_ZERO); memcpy(us->desc, ks->desc, sizeof(us->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;*/ - ret = sysctl_rdstruct(oldp, oldlenp, newp, us, + ret = sysctl_rdstruct(oldp, oldlenp, NULL, us, sizeof(struct sensor)); free(us, M_TEMP); return (ret); } int sysctl_emul(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) Index: sbin/sysctl/sysctl.c =================================================================== RCS file: /cvs/src/sbin/sysctl/sysctl.c,v retrieving revision 1.160 diff -u -d -p -8 -r1.160 sysctl.c --- sbin/sysctl/sysctl.c 4 Aug 2008 04:26:42 -0000 1.160 +++ sbin/sysctl/sysctl.c 8 May 2009 18:47:09 -0000 @@ -465,16 +465,19 @@ parse(char *string, int flags) warnx("use vmstat to view %s information", string); return; case HW_SENSORS: special |= SENSORS; len = sysctl_sensors(string, &bufp, mib, flags, &type); if (len < 0) return; + if (newsize > 0) + /* XXX: make this more intelligent */ + type = CTLTYPE_INT; break; case HW_PHYSMEM: case HW_USERMEM: /* * Don't print these; we'll print the 64-bit * variants instead. */ return; @@ -2412,16 +2415,19 @@ print_sensor(struct sensor *s) break; 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: Index: sys/dev/ic/lm78var.h =================================================================== RCS file: /cvs/src/sys/dev/ic/lm78var.h,v retrieving revision 1.14 diff -u -d -p -8 -r1.14 lm78var.h --- sys/dev/ic/lm78var.h 25 Jun 2007 22:50:18 -0000 1.14 +++ sys/dev/ic/lm78var.h 8 May 2009 18:47:09 -0000 @@ -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; Index: sys/dev/ic/lm78.c =================================================================== RCS file: /cvs/src/sys/dev/ic/lm78.c,v retrieving revision 1.20 diff -u -d -p -8 -r1.20 lm78.c --- sys/dev/ic/lm78.c 25 Jun 2007 22:50:18 -0000 1.20 +++ sys/dev/ic/lm78.c 8 May 2009 18:47:10 -0000 @@ -1,12 +1,13 @@ /* $OpenBSD: lm78.c,v 1.20 2007/06/25 22:50:18 cnst Exp $ */ /* * Copyright (c) 2005, 2006 Mark Kettenis + * Copyright (c) 2006, 2009 Constantine A. Murenin * * 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 @@ -21,16 +22,20 @@ #include #include #include #include #include #include +//#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. @@ -55,16 +60,24 @@ void lm_setup_sensors(struct lm_softc *, 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); @@ -95,17 +108,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) }, @@ -128,93 +147,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 +662,17 @@ lm_attach(struct lm_softc *sc) for (i = 0; i < sizeof(lm_chips) / sizeof(lm_chips[0]); i++) if (lm_chips[i].chip_match(sc)) break; /* No point in doing anything if we don't have any sensors. */ if (sc->numsensors == 0) return; - sc->sensortask = sensor_task_register(sc, lm_refresh, 5); + sc->sensortask = sensor_task_register(sc, lm_refresh, 3); if (sc->sensortask == NULL) { printf("%s: unable to register update task\n", sc->sc_dev.dv_xname); return; } /* Start the monitoring loop */ config = sc->lm_readreg(sc, LM_CONFIG); @@ -498,17 +766,21 @@ wb_match(struct lm_softc *sc) DPRINTF((" winbond chip id 0x%x\n", sc->chipid)); switch(sc->chipid) { case WB_CHIPID_W83627HF: printf(": W83627HF\n"); lm_setup_sensors(sc, w83627hf_sensors); break; case WB_CHIPID_W83627THF: printf(": W83627THF\n"); - 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_A: printf(": W83627EHF-A\n"); lm_setup_sensors(sc, w83627ehf_sensors); break; case WB_CHIPID_W83627EHF: printf(": W83627EHF\n"); lm_setup_sensors(sc, w83627ehf_sensors); @@ -518,17 +790,17 @@ wb_match(struct lm_softc *sc) lm_setup_sensors(sc, w83627dhg_sensors); break; case WB_CHIPID_W83637HF: printf(": W83637HF\n"); 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: printf(": W83697HF\n"); lm_setup_sensors(sc, w83697hf_sensors); break; case WB_CHIPID_W83781D: case WB_CHIPID_W83781D_2: printf(": W83781D\n"); @@ -584,16 +856,22 @@ lm_setup_sensors(struct lm_softc *sc, st strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sensordev.xname)); 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) { @@ -690,16 +968,434 @@ wb_refresh_sensor_data(struct lm_softc * for (i = 0; i < sc->numsensors; i++) { if (bank != sc->lm_sensors[i].bank) { bank = sc->lm_sensors[i].bank; 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; + } + + 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; + snprintf(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; + } + + 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; + } + + 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; + snprintf(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; + } + + 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; + } + + 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; + } + + 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; + snprintf(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;