Subject: kern/16118: Some bugfixes/enhancements for the lm7x driver
To: None <gnats-bugs@gnats.netbsd.org>
From: None <Thilo.Manske@HEH.Uni-Oldenburg.DE>
List: netbsd-bugs
Date: 03/29/2002 22:35:17
>Number:         16118
>Category:       kern
>Synopsis:       Some bufixes/enhancements for the lm8x driver
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Mar 29 13:36:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Thilo Manske
>Release:        NetBSD 1.5ZC
>Organization:
Dies ist Thilos Unix Signature! Viel Spass damit.
>Environment:
System: NetBSD WintelKiller 1.5ZC NetBSD 1.5ZC (WintelKiller) #341: Fri Mar 29 17:38:25 MET 2002 thilo@WintelKiller:/usr/src/sys/arch/i386/compile/WintelKiller i386
Architecture: i386
Machine: i386
>Description:
There are several bugs with our current lm7x driver treating fan sensors:

1. for hardware monitors which use the generic_streinfo routines setting
any of the nominal fan speeds (with ENVSYS_STREINFO) will result in setting
FAN2's divisor (mixing fan number with sensor number in the code)
2. the Winbond 83781D does have a programmable divisor for FAN3 unlike generic lm
sensors (and thus needs special treatment)
3. the nominal RPMs will never be set anywhere?!?
4. a bug/typo in the code for setting FAN3's divisor for W83782 type
hardware monitors

>How-To-Repeat:
>Fix:
Index: nslm7x.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/nslm7x.c,v
retrieving revision 1.13
diff -c -u -r1.13 nslm7x.c
--- nslm7x.c	2001/11/13 13:14:42	1.13
+++ nslm7x.c	2002/03/29 21:17:24
@@ -103,6 +103,7 @@
 
 static void wb_svolt __P((struct lm_softc *));
 static void wb_stemp __P((struct lm_softc *, struct envsys_tre_data *, int));
+static void wb781_fanrpm __P((struct lm_softc *, struct envsys_tre_data *));
 static void wb_fanrpm __P((struct lm_softc *, struct envsys_tre_data *));
 
 void wb781_refresh_sensor_data __P((struct lm_softc *));
@@ -467,12 +468,15 @@
 
 	/* FAN1 and FAN2 can have divisors set, but not FAN3 */
 	if ((sc->info[binfo->sensor].units == ENVSYS_SFANRPM)
-	    && (binfo->sensor != 2)) {
+	    && (n < 2)) {
 		if (binfo->rpms == 0) {
 			binfo->validflags = 0;
 			return (0);
 		}
 
+		/* write back the nominal FAN speed  */
+		info->rpms =binfo->rpms;
+
 		/* 153 is the nominal FAN speed value */
 		divisor = 1350000 / (binfo->rpms * 153);
 
@@ -491,7 +495,7 @@
 		 * in <7:6>
 		 */
 		sdata = lm_readreg(sc, LMD_VIDFAN);
-		if ( binfo->sensor == 0 ) {  /* FAN1 */
+		if ( n == 0 ) {  /* FAN1 */
 		    divisor <<= 4;
 		    sdata = (sdata & 0xCF) | divisor;
 		} else { /* FAN2 */
@@ -535,13 +539,53 @@
 	 struct envsys_basic_info *binfo;
 {
 	 struct lm_softc *sc = sme->sme_cookie;
+	 int divisor;
+	 u_int8_t sdata;
+	 int i;
 
 	 if (sc->info[binfo->sensor].units == ENVSYS_SVOLTS_DC)
 		  sc->info[binfo->sensor].rfact = binfo->rfact;
 	 else {
 		if (sc->info[binfo->sensor].units == ENVSYS_SFANRPM) {
-			generic_streinfo_fan(sc, &sc->info[binfo->sensor],
-			    binfo->sensor - 10, binfo);
+			if (binfo->rpms == 0) {
+				binfo->validflags = 0;
+				return (0);
+			}
+
+			/* write back the nominal FAN speed  */
+			sc->info[binfo->sensor].rpms=binfo->rpms;
+
+			/* 153 is the nominal FAN speed value */
+			divisor = 1350000 / (binfo->rpms * 153);
+
+			/* ...but we need lg(divisor) */
+			for (i = 0; i < 7; i++) {
+				if (divisor <= (1 << i))
+				 	break;
+			}
+			divisor = i;
+
+			if (binfo->sensor == 10 || binfo->sensor == 11) {
+				/*
+				 * FAN1 div is in bits <5:4>, FAN2 div
+				 * is in <7:6>
+				 */
+				sdata = lm_readreg(sc, LMD_VIDFAN);
+				if ( binfo->sensor == 10 ) {  /* FAN1 */
+					 sdata = (sdata & 0xCF) |
+					     ((divisor & 0x3) << 4);
+				} else { /* FAN2 */
+					 sdata = (sdata & 0x3F) |
+					     ((divisor & 0x3) << 6);
+				}
+				lm_writereg(sc, LMD_VIDFAN, sdata);
+			} else {
+				/* FAN3 is in WB_PIN <7:6> */
+				sdata = lm_readreg(sc, WB_PIN);
+				sdata = (sdata & 0x3F) |
+				     ((divisor & 0x3) << 6);
+				lm_writereg(sc, WB_PIN, sdata);
+			}
 		}
 		memcpy(sc->info[binfo->sensor].desc, binfo->desc,
 		    sizeof(sc->info[binfo->sensor].desc));
@@ -572,6 +616,9 @@
 				return (0);
 			}
 
+			/* write back the nominal FAN speed  */
+			sc->info[binfo->sensor].rpms=binfo->rpms;
+
 			/* 153 is the nominal FAN speed value */
 			divisor = 1350000 / (binfo->rpms * 153);
 
@@ -601,7 +648,7 @@
 				sdata = lm_readreg(sc, WB_PIN);
 				sdata = (sdata & 0x3F) |
 				     ((divisor & 0x3) << 6);
-				lm_writereg(sc, LMD_VIDFAN, sdata);
+				lm_writereg(sc, WB_PIN, sdata);
 			}
 			/* Bit 2 of divisor is in WB_BANK0_FANBAT */
 			lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
@@ -760,6 +807,33 @@
 }
 
 static void
+wb781_fanrpm(sc, sensors)
+	struct lm_softc *sc;
+	struct envsys_tre_data *sensors;
+{
+	int i, divisor, sdata;
+	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+	for (i = 0; i < 3; i++) {
+		sdata = lm_readreg(sc, LMD_SENSORBASE + i + 8);
+		DPRINTF(("sdata[fan%d] 0x%x\n", i, sdata));
+		if (i == 0)
+			divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) & 0x3;
+		else if (i == 1)
+			divisor = (lm_readreg(sc, LMD_VIDFAN) >> 6) & 0x3;
+		else
+			divisor = (lm_readreg(sc, WB_PIN) >> 6) & 0x3;
+
+		DPRINTF(("sdata[%d] 0x%x div 0x%x\n", i, sdata, divisor));
+		if (sdata == 0xff || sdata == 0x00) {
+			sensors[i].cur.data_us = 0;
+		} else {
+			sensors[i].cur.data_us = 1350000 /
+			    (sdata << divisor);
+		}
+	}
+}
+
+static void
 wb_fanrpm(sc, sensors)
 	struct lm_softc *sc;
 	struct envsys_tre_data *sensors;
@@ -798,7 +872,7 @@
 	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
 	wb_stemp(sc, &sc->sensors[7], 3);
 	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
-	generic_fanrpm(sc, &sc->sensors[10]);
+	wb781_fanrpm(sc, &sc->sensors[10]);
 }
 
 void
>Release-Note:
>Audit-Trail:
>Unformatted: