Subject: kern/26420: lm(4): Add support vor environment controller built into iTE 8705f
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <Thilo.Manske@HEH.Uni-Oldenburg.DE>
List: netbsd-bugs
Date: 07/24/2004 18:10:54
>Number:         26420
>Category:       kern
>Synopsis:       lm(4): Add support vor environment controller built into iTE 8705f
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Jul 24 16:43:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Thilo Manske
>Release:        NetBSD 2.0G
>Organization:
>Environment:
System: NetBSD WintelKiller 2.0G NetBSD 2.0G (WintelKiller) #426: Sat Jul 10 21:50:32 MEST 2004 thilo@WintelKiller:/sys/arch/i386/compile/WintelKiller i386
Architecture: i386
Machine: i386
>Description:
This patch adds support for the environment controller built into the IT870F
super i/o chip from iTE found on some i386 motherboards.

Compared to LM or Winbond chips this environment controller has a completely
different internal register setup but otherwise works pretty much the same
way.
>How-To-Repeat:
>Fix:
Index: sys/dev/ic/nslm7x.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/nslm7x.c,v
retrieving revision 1.18
diff -c -u -r1.18 nslm7x.c
--- sys/dev/ic/nslm7x.c	22 Apr 2004 00:17:11 -0000	1.18
+++ sys/dev/ic/nslm7x.c	24 Jul 2004 16:09:44 -0000
@@ -88,6 +88,7 @@
 
 int lm_match __P((struct lm_softc *));
 int wb_match __P((struct lm_softc *));
+int itec_match __P((struct lm_softc *));
 int def_match __P((struct lm_softc *));
 void lm_common_match __P((struct lm_softc *));
 static int lm_generic_banksel __P((struct lm_softc *, int));
@@ -108,6 +109,12 @@
 void wb782_refresh_sensor_data __P((struct lm_softc *));
 void wb697_refresh_sensor_data __P((struct lm_softc *));
 
+static void itec_svolt __P((struct lm_softc *, struct envsys_tre_data *,
+    struct envsys_basic_info *));
+static void itec_stemp __P((struct lm_softc *, struct envsys_tre_data *));
+static void itec_fanrpm __P((struct lm_softc *, struct envsys_tre_data *));
+void itec_refresh_sensor_data __P((struct lm_softc *));
+
 int lm_gtredata __P((struct sysmon_envsys *, struct envsys_tre_data *));
 
 int generic_streinfo_fan __P((struct lm_softc *, struct envsys_basic_info *,
@@ -115,12 +122,14 @@
 int lm_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
 int wb781_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
 int wb782_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
+int itec_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
 
 struct lm_chip {
 	int (*chip_match) __P((struct lm_softc *));
 };
 
 struct lm_chip lm_chips[] = {
+	{ itec_match },
 	{ wb_match },
 	{ lm_match },
 	{ def_match } /* Must be last */
@@ -149,6 +158,23 @@
 	u_int8_t cr;
 	int rv;
 
+	/*
+	 * Check for it8705f, bevor we do the chip reset.
+	 * In case of an it8705f this might reset all the fan control
+	 * parameters to defaults which would void all settings done by
+	 * the BOOTROM/BIOS.
+	 */
+	bus_space_write_1(iot, ioh, LMC_ADDR, ITEC_RES48);
+	cr = bus_space_read_1(iot, ioh, LMC_DATA);
+
+	if (cr==ITEC_RES48_DEFAULT)
+	{
+		bus_space_write_1(iot, ioh, LMC_ADDR, ITEC_RES52);
+		cr = bus_space_read_1(iot, ioh, LMC_DATA);
+		if (cr==ITEC_RES52_DEFAULT)
+			return 1;
+	}
+
 	/* Check for some power-on defaults */
 	bus_space_write_1(iot, ioh, LMC_ADDR, LMD_CONFIG);
 
@@ -398,6 +424,47 @@
 	sc->info[8].rfact = 10000;
 }
 
+int
+itec_match(sc)
+	struct lm_softc *sc;
+{
+	int vendor;
+	/* do the same thing as in  lm_probe() */
+	if ((*sc->lm_readreg)(sc, ITEC_RES48)!=ITEC_RES48_DEFAULT)
+		return 0;
+
+	if ((*sc->lm_readreg)(sc, ITEC_RES52)!=ITEC_RES52_DEFAULT)
+		return 0;
+
+	vendor=(*sc->lm_readreg)(sc, ITEC_VENDID);
+
+	if (vendor==ITEC_VENDID_ITE)
+		printf(": iTE IT8705f\n");
+	else
+		printf(": unknown IT8705f compatible, vendorid 0x%02x\n",vendor);
+
+	/* XXX this is a litle bit lame...
+	 * All VIN inputs work exactly the same way, it depends of the
+	 * external wiring what voltages they monitor and which correction
+	 * factors are needed. We assume a pretty standard setup here
+	 */
+	wb_setup_volt(sc);
+	snprintf(sc->info[0].desc, sizeof(sc->info[0].desc), "CPU");
+	snprintf(sc->info[1].desc, sizeof(sc->info[1].desc), "AGP");
+	snprintf(sc->info[6].desc, sizeof(sc->info[1].desc), "+2.5V");
+	sc->info[5].rfact=51100;
+	sc->info[7].rfact=16778;
+
+	setup_temp(sc,9,3);
+	setup_fan(sc,12,3);
+	sc->numsensors = ITEC_NUM_SENSORS;
+	sc->refresh_sensor_data = itec_refresh_sensor_data;
+	sc->sc_sysmon.sme_streinfo = itec_streinfo;
+
+	return 1;
+}
+
+
 static void
 setup_temp(sc, start, n)
 	struct lm_softc *sc;
@@ -667,6 +734,65 @@
 	return (0);
 }
 
+int
+itec_streinfo(sme, binfo)
+	 struct sysmon_envsys *sme;
+	 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) {
+			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;
+
+			sdata = (*sc->lm_readreg)(sc, ITEC_FANDIV);
+			/*
+			 * FAN1 div is in bits <0:2>, FAN2 is in <3:5>
+			 * FAN3 is in <6>, if set divisor is 8, else 2
+			 */
+			if ( binfo->sensor == 10 ) {  /* FAN1 */
+				 sdata = (sdata & 0xf8) | divisor;
+			} else if ( binfo->sensor == 11 ) { /* FAN2 */
+				 sdata = (sdata & 0xc7) | divisor << 3;
+			} else { /* FAN3 */
+				if (divisor>2)
+					sdata = sdata & 0xbf;
+				else
+					sdata = sdata | 0x40;
+			}
+			(*sc->lm_writereg)(sc, ITEC_FANDIV, sdata);
+		}
+		memcpy(sc->info[binfo->sensor].desc, binfo->desc,
+		    sizeof(sc->info[binfo->sensor].desc));
+		sc->info[binfo->sensor].desc[
+		    sizeof(sc->info[binfo->sensor].desc) - 1] = '\0';
+
+		binfo->validflags = ENVSYS_FVALID;
+	 }
+	 return (0);
+}
+
 static void
 generic_stemp(sc, sensor)
 	struct lm_softc *sc;
@@ -899,3 +1025,88 @@
 	wb_stemp(sc, &sc->sensors[9], 2);
 	wb_fanrpm(sc, &sc->sensors[11]);
 }
+
+static void
+itec_svolt(sc, sensors, infos)
+	struct lm_softc *sc;
+	struct envsys_tre_data *sensors;
+	struct envsys_basic_info *infos;
+{
+	int i, sdata;
+
+	for (i = 0; i < 9; i++) {
+		sdata = (*sc->lm_readreg)(sc, ITEC_VIN0 + i);
+		DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata));
+		/* voltage returned as (mV >> 4), we convert to uVDC */
+		sensors[i].cur.data_s = ( sdata << 4 );
+		/* rfact is (factor * 10^4) */
+
+		sensors[i].cur.data_s *= infos[i].rfact;
+		/*
+		 * XXX We assume input 5 is wired the way iTE suggests to
+		 * monitor a negative voltage. I'd prefer using negative rfacts
+		 * for detecting those cases but since rfact is an u_int this
+		 * isn't possible.
+		 */
+		if (i==5) {
+			sensors[i].cur.data_s-= ( infos[i].rfact - 10000 )* ITEC_VREF;
+		}
+		/* division by 10 gets us back to uVDC */
+		sensors[i].cur.data_s /= 10;
+	}
+}
+
+static void
+itec_stemp(sc, sensors)
+	struct lm_softc *sc;
+	struct  envsys_tre_data *sensors;
+{
+	int i, sdata;
+
+	/* temperatures. Given in dC, we convert to uK */
+	for (i = 0; i < 3; i++)
+	{
+		sdata = (*sc->lm_readreg)(sc, ITEC_TEMP1 + i );
+		DPRINTF(("sdata[temp%d] 0x%x\n",i, sdata));
+		sensors[i].cur.data_us = sdata * 1000000 + 273150000;
+	}
+}
+
+static void
+itec_fanrpm(sc, sensors)
+	struct lm_softc *sc;
+	struct envsys_tre_data *sensors;
+{
+	int i, fandiv, divisor, sdata;
+	(*sc->lm_banksel)(sc, 0);
+	fandiv=((*sc->lm_readreg)(sc, ITEC_FANDIV));
+
+	for (i = 0; i < 3; i++) {
+		sdata = (*sc->lm_readreg)(sc, ITEC_FAN1 + i);
+		DPRINTF(("sdata[fan%d] 0x%x\n", i, sdata));
+		if (i == 0)
+			divisor = fandiv & 0x7;
+		else if (i == 1)
+			divisor = (fandiv >> 3) & 0x7;
+		else /* (i == 2) */
+			divisor = (fandiv && 0x40)?3:1;
+
+		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);
+		}
+	}
+
+}
+
+void
+itec_refresh_sensor_data(sc)
+	struct lm_softc *sc;
+{
+	itec_svolt(sc, &sc->sensors[0], &sc->info[0]);
+	itec_stemp(sc, &sc->sensors[9]);
+	itec_fanrpm(sc, &sc->sensors[12]);
+}
Index: sys/dev/ic/nslm7xvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/nslm7xvar.h,v
retrieving revision 1.11
diff -c -u -r1.11 nslm7xvar.h
--- sys/dev/ic/nslm7xvar.h	2 Nov 2003 11:07:45 -0000	1.11
+++ sys/dev/ic/nslm7xvar.h	24 Jul 2004 16:09:45 -0000
@@ -116,6 +116,40 @@
 #define WB83697_NUM_SENSORS	14
 #define WB_NUM_SENSORS	15
 
+/*
+ * registers for the environment controller built into
+ * the IT8705F super-i/o chip
+ */
+#define ITEC_FANDIV	0x0b	/* fan divisor */
+#define ITEC_FAN1	0x0d	/* fan1 tachometer */
+#define ITEC_FAN2	0x0e	/* fan2 tachometer */
+#define ITEC_FAN3	0x0f	/* fan3 tachometer */
+#define ITEC_VIN0	0x20	/* VIN0 voltage */
+#define ITEC_VIN1	0x21	/* VIN1 voltage */
+#define ITEC_VIN2	0x22	/* VIN2 voltage */
+#define ITEC_VIN3	0x23	/* VIN3 voltage */
+#define ITEC_VIN4	0x24	/* VIN4 voltage */
+#define ITEC_VIN5	0x25	/* VIN5 voltage */
+#define ITEC_VIN6	0x26	/* VIN6 voltage */
+#define ITEC_VIN7	0x27	/* VIN7 voltage */
+#define ITEC_VBAT	0x28	/* VBAT voltage */
+#define ITEC_TEMP1	0x29	/* TMPIN1 temperature */
+#define ITEC_TEMP2	0x30	/* TMPIN1 temperature */
+#define ITEC_TEMP3	0x31	/* TMPIN1 temperature */
+#define ITEC_RES48	0x48	/* reserved, used for probing the chip */
+#define ITEC_RES52	0x52	/* reserved, used for probing the chip */
+#define ITEC_VENDID	0x58	/* vendor ID register */
+
+/*
+ * misc values
+ */
+#define ITEC_VENDID_ITE	0x90	/* iTE vendor ID */
+#define ITEC_RES48_DEFAULT	0x2d
+#define ITEC_RES52_DEFAULT	0x7f
+#define ITEC_NUM_SENSORS	15
+#define ITEC_VREF	4096		/* VREF in mV */
+
+
 struct lm_softc {
 	struct	device sc_dev;
 
>Release-Note:
>Audit-Trail:
>Unformatted: