Subject: lm7x & W83627HF
To: None <tech-kern@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-kern
Date: 07/24/2000 15:10:16
--3MwIy2ne0vdjdPXF
Content-Type: text/plain; charset=us-ascii

Hi,
Unless someone objects I'm going to commit the attached patch to the nslm7x
driver. It adds support for the system monitor part of the winbond W83627HF
chip (used on some i810-based motherboards). This chip is lm7x compatible
but has extended capabilities and more sensors.
It also make supporting other lm7x-like chips easier :)

--
Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
--

--3MwIy2ne0vdjdPXF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff

Index: nslm7x.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/nslm7x.c,v
retrieving revision 1.4
diff -u -b -r1.4 nslm7x.c
--- nslm7x.c	2000/06/24 00:37:19	1.4
+++ nslm7x.c	2000/07/24 13:06:41
@@ -1,4 +1,4 @@
-/*	$NetBSD: nslm7x.c,v 1.4 2000/06/24 00:37:19 thorpej Exp $ */
+/*	$NetBSD: nslm7x.c,v 1.3 2000/03/09 04:20:58 groo Exp $ */
 
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -81,9 +81,30 @@
 	
 u_int8_t lm_readreg __P((struct lm_softc *, int));
 void lm_writereg __P((struct lm_softc *, int, int));
+
+int lm_match __P((struct lm_softc *));
 void lm_refresh_sensor_data __P((struct lm_softc *));
+
+int wb_match __P((struct lm_softc *));
+void wb_refresh_sensor_data __P((struct lm_softc *));
+
+int def_match __P((struct lm_softc *));
+void lm_common_match __P((struct lm_softc *));
+
 int lm_gtredata __P((struct sysmon_envsys *, struct envsys_tre_data *));
 int lm_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
+int wb_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
+
+struct lm_chip {
+	int (*chip_match) __P((struct lm_softc *));
+};
+
+struct lm_chip lm_chips[] = {
+	{ lm_match},
+	{ wb_match},
+	{ def_match} /* Must be last */
+};
+
 
 u_int8_t
 lm_readreg(sc, reg)
@@ -147,17 +168,9 @@
 {
 	int i;
 
-	/* See if we have an LM78 or LM79 */
-	i = lm_readreg(lmsc, LMD_CHIPID) & LM_ID_MASK;
-	printf(": LM7");
-	if (i == LM_ID_LM78)
-		printf("8\n");
-	else if (i == LM_ID_LM78J)
-		printf("8J\n");
-	else if (i == LM_ID_LM79)
-		printf("9\n");
-	else
-		printf("? - Unknown chip ID (%x)\n", i);
+	for (i = 0; i < sizeof(lm_chips) / sizeof(lm_chips[0]); i++)
+		if (lm_chips[i].chip_match(lmsc))
+			break;
 
 	/* Start the monitoring loop */
 	lm_writereg(lmsc, LMD_CONFIG, 0x01);
@@ -166,45 +179,12 @@
 	timerclear(&lmsc->lastread);
 
 	/* Initialize sensors */
-	for (i = 0; i < LM_NUM_SENSORS; ++i) {
+	for (i = 0; i < lmsc->numsensors; ++i) {
 		lmsc->sensors[i].sensor = lmsc->info[i].sensor = i;
 		lmsc->sensors[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
 		lmsc->info[i].validflags = ENVSYS_FVALID;
 		lmsc->sensors[i].warnflags = ENVSYS_WARN_OK;
 	}
-
-	for (i = 0; i < 7; ++i) {
-		lmsc->sensors[i].units = lmsc->info[i].units =
-		    ENVSYS_SVOLTS_DC;
-
-		lmsc->info[i].desc[0] = 'I';
-		lmsc->info[i].desc[1] = 'N';
-		lmsc->info[i].desc[2] = i + '0';
-		lmsc->info[i].desc[3] = 0;
-	}
-
-	/* default correction factors for resistors on higher voltage inputs */
-	lmsc->info[0].rfact = lmsc->info[1].rfact = 
-	    lmsc->info[2].rfact = 10000;
-	lmsc->info[3].rfact = (int)(( 90.9 / 60.4) * 10000);
-	lmsc->info[4].rfact = (int)(( 38.0 / 10.0) * 10000);
-	lmsc->info[5].rfact = (int)((210.0 / 60.4) * 10000);
-	lmsc->info[6].rfact = (int)(( 90.9 / 60.4) * 10000);
-
-	lmsc->sensors[7].units = ENVSYS_STEMP;
-	strcpy(lmsc->info[7].desc, "Temp");
-
-	for (i = 8; i < 11; ++i) {
-		lmsc->sensors[i].units = lmsc->info[i].units = ENVSYS_SFANRPM;
-
-		lmsc->info[i].desc[0] = 'F';
-		lmsc->info[i].desc[1] = 'a';
-		lmsc->info[i].desc[2] = 'n';
-		lmsc->info[i].desc[3] = ' ';
-		lmsc->info[i].desc[4] = i - 7 + '0';
-		lmsc->info[i].desc[5] = 0;
-	}
-
 	/*
 	 * Hook into the System Monitor.
 	 */
@@ -214,9 +194,9 @@
 	lmsc->sc_sysmon.sme_cookie = lmsc;
 
 	lmsc->sc_sysmon.sme_gtredata = lm_gtredata;
-	lmsc->sc_sysmon.sme_streinfo = lm_streinfo;
+	/* sme_streinfo set in chip-specific attach */
 
-	lmsc->sc_sysmon.sme_nsensors = LM_NUM_SENSORS;
+	lmsc->sc_sysmon.sme_nsensors = lmsc->numsensors;
 	lmsc->sc_sysmon.sme_envsys_version = 1000;
 
 	if (sysmon_envsys_register(&lmsc->sc_sysmon))
@@ -224,8 +204,138 @@
 		    lmsc->sc_dev.dv_xname);
 }
 
+int
+lm_match(sc)
+	struct lm_softc *sc;
+{
+	int i;
 
+	/* See if we have an LM78 or LM79 */
+	i = lm_readreg(sc, LMD_CHIPID) & LM_ID_MASK;
+	switch(i) {
+	case LM_ID_LM78:
+		printf(": LM78\n");
+		break;
+	case LM_ID_LM78J:
+		printf(": LM78J\n");
+		break;
+	case LM_ID_LM79:
+		printf(": LM79\n");
+		break;
+	default:
+		return 0;
+	}
+	lm_common_match(sc);
+	return 1;
+}
+
 int
+def_match(sc)
+	struct lm_softc *sc;
+{
+	int i;
+
+	i = lm_readreg(sc, LMD_CHIPID) & LM_ID_MASK;
+	printf(": Unknow chip (ID %d)\n", i);
+	lm_common_match(sc);
+	return 1;
+}
+
+void
+lm_common_match(sc)
+	struct lm_softc *sc;
+{
+	int i;
+	sc->numsensors = LM_NUM_SENSORS;
+	sc->refresh_sensor_data = lm_refresh_sensor_data;
+
+	for (i = 0; i < 7; ++i) {
+		sc->sensors[i].units = sc->info[i].units =
+		    ENVSYS_SVOLTS_DC;
+		sprintf(sc->info[i].desc, "IN %d", i);
+	}
+
+	/* default correction factors for resistors on higher voltage inputs */
+	sc->info[0].rfact = sc->info[1].rfact =
+	    sc->info[2].rfact = 10000;
+	sc->info[3].rfact = (int)(( 90.9 / 60.4) * 10000);
+	sc->info[4].rfact = (int)(( 38.0 / 10.0) * 10000);
+	sc->info[5].rfact = (int)((210.0 / 60.4) * 10000);
+	sc->info[6].rfact = (int)(( 90.9 / 60.4) * 10000);
+
+	sc->sensors[7].units = ENVSYS_STEMP;
+	strcpy(sc->info[7].desc, "Temp");
+
+	for (i = 8; i < 11; ++i) {
+		sc->sensors[i].units = sc->info[i].units = ENVSYS_SFANRPM;
+		sprintf(sc->info[i].desc, "Fan %d", i - 7);
+	}
+	sc->sc_sysmon.sme_streinfo = lm_streinfo;
+}
+
+int
+wb_match(sc)
+	struct lm_softc *sc;
+{
+	int i, j;
+
+	/* See if we have a winbond */
+	i = lm_readreg(sc, LMD_CHIPID) & LM_ID_MASK;
+	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_HBAC);
+	j = lm_readreg(sc, WB_VENDID) << 8;
+	lm_writereg(sc, WB_BANKSEL, 0);
+	j |= lm_readreg(sc, WB_VENDID);
+	DPRINTF(("winbond vend id %d\n", j));
+	if (j != WB_VENDID_WINBOND)
+		return 0;
+	printf(": W83627HF (device ID %d)\n", i);
+	sc->numsensors = WB_NUM_SENSORS;
+	sc->refresh_sensor_data = wb_refresh_sensor_data;
+
+	sc->sensors[0].units = sc->info[0].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[0].desc, "VCORE A");
+	sc->info[0].rfact = 10000;
+	sc->sensors[1].units = sc->info[1].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[1].desc, "VCORE B");
+	sc->info[1].rfact = 10000;
+	sc->sensors[2].units = sc->info[2].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[2].desc, "+3.3V");
+	sc->info[2].rfact = 10000;
+	sc->sensors[3].units = sc->info[3].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[3].desc, "+5V");
+	sc->info[3].rfact = 16778;
+	sc->sensors[4].units = sc->info[4].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[4].desc, "+12V");
+	sc->info[4].rfact = 38000;
+	sc->sensors[5].units = sc->info[5].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[5].desc, "-12V");
+	sc->info[5].rfact = 10000;
+	sc->sensors[6].units = sc->info[6].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[6].desc, "-5V");
+	sc->info[6].rfact = 10000;
+	sc->sensors[7].units = sc->info[7].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[7].desc, "+5VSB");
+	sc->info[7].rfact = 15151;
+	sc->sensors[8].units = sc->info[8].units = ENVSYS_SVOLTS_DC;
+	sprintf(sc->info[8].desc, "VBAT");
+	sc->info[8].rfact = 10000;
+
+	sc->sensors[9].units = ENVSYS_STEMP;
+	strcpy(sc->info[9].desc, "Temp 1");
+	sc->sensors[10].units = ENVSYS_STEMP;
+	strcpy(sc->info[10].desc, "Temp 2");
+	sc->sensors[11].units = ENVSYS_STEMP;
+	strcpy(sc->info[11].desc, "Temp 3");
+
+	for (i = 12; i < 15; ++i) {
+		sc->sensors[i].units = sc->info[i].units = ENVSYS_SFANRPM;
+		sprintf(sc->info[i].desc, "Fan %d", i - 11);
+	}
+	sc->sc_sysmon.sme_streinfo = wb_streinfo;
+	return 1;
+}
+
+int
 lm_gtredata(sme, tred)
 	struct sysmon_envsys *sme;
 	struct envsys_tre_data *tred;
@@ -246,14 +356,13 @@
 	splx(s);
    
 	if (i)
-		lm_refresh_sensor_data(sc);
+		  sc->refresh_sensor_data(sc);
 
 	*tred = sc->sensors[tred->sensor];
 
 	return (0);
 }
 
-
 int
 lm_streinfo(sme, binfo)
 	struct sysmon_envsys *sme;
@@ -314,7 +423,75 @@
 	return (0);
 }
 
+int
+wb_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);
+			}
+
+			/* 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 == 12 || binfo->sensor == 13) {
+				/*
+				 * FAN1 div is in bits <5:4>, FAN2 div
+				 * is in <7:6>
+				 */
+				sdata = lm_readreg(sc, LMD_VIDFAN);
+				if ( binfo->sensor == 12 ) {  /* 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, LMD_VIDFAN, sdata);
+			}
+			/* Bit 2 of divisor is in WB_BANK0_FANBAT */
+			lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+			sdata = lm_readreg(sc, WB_BANK0_FANBAT);
+			sdata &= ~(0x20 << (binfo->sensor - 12));
+			sdata |= (divisor & 0x4) << (binfo->sensor - 9);
+			lm_writereg(sc, WB_BANK0_FANBAT, 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);
+}
+
 /*
  * pre:  last read occured >= 1.5 seconds ago
  * post: sensors[] current data are the latest from the chip
@@ -323,7 +500,7 @@
 lm_refresh_sensor_data(sc)
 	struct lm_softc *sc;
 {
-	u_int8_t sdata;
+	int sdata;
 	int i, divisor;
 
 	/* Refresh our stored data for every sensor */
@@ -361,9 +538,12 @@
 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) &
 				    0x3;
 			
+			if (sdata == 0xff || sdata == 0x00) {
+				sc->sensors[i].cur.data_us = 0;
+			} else {
 			sc->sensors[i].cur.data_us = 1350000 /
 			    (sdata << divisor);
-			
+			}
 			break;
 			
 		default:
@@ -371,6 +551,85 @@
 			sc->sensors[i].validflags = 0;
 			
 			break;
+		}
+	}
+}
+
+void
+wb_refresh_sensor_data(sc)
+	struct lm_softc *sc;
+{
+	int sdata;
+	int i, divisor;
+
+	/* Refresh our stored data for every sensor */
+	/* first voltage sensors */
+	for (i = 0; i < 9; ++i) {
+		if (i < 7) {
+			sdata = lm_readreg(sc, LMD_SENSORBASE + i);
+		} else {
+			/* from bank5 */
+			lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B5);
+			sdata = lm_readreg(sc, (i == 7) ?
+			    WB_BANK5_5VSB : WB_BANK5_VBAT);
+		}
+		DPRINTF(("sdata[%d] 0x%x\n", i, sdata));
+		/* voltage returned as (mV >> 4), we convert to uV */
+		sdata =  sdata << 4;
+		/* special case for negative voltages */
+		if (i == 5) {
+			/*
+			 * -12Vdc, assume Winbond recommended values for
+			 * resistors
+			 */
+			sdata = ((sdata * 1000) - (3600 * 805)) / 195;
+		} else if (i == 6) {
+			/*
+			 * -5Vdc, assume Winbond recommended values for
+			 * resistors
+			 */
+			sdata = ((sdata * 1000) - (3600 * 682)) / 318;
+		}
+		/* rfact is (factor * 10^4) */
+		sc->sensors[i].cur.data_s = sdata * sc->info[i].rfact;
+		/* division by 10 gets us back to uVDC */
+		sc->sensors[i].cur.data_s /= 10;
+	}
+	/* temperatures. Given in dC, we convert to uK */
+	sdata = lm_readreg(sc, LMD_SENSORBASE + 7);
+	DPRINTF(("sdata[%d] 0x%x\n", 9, sdata));
+	sc->sensors[9].cur.data_us = sdata * 1000000 + 273150000;
+	/* from bank1 */
+	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B1);
+	sdata = lm_readreg(sc, WB_BANK1_T2H) << 1;
+	sdata |=  (lm_readreg(sc, WB_BANK1_T2L) & 0x80) >> 7;
+	DPRINTF(("sdata[%d] 0x%x\n", 10, sdata));
+	sc->sensors[10].cur.data_us = (sdata * 1000000) / 2 + 273150000;
+	/* from bank2 */
+	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B2);
+	sdata = lm_readreg(sc, WB_BANK2_T3H) << 1;
+	sdata |=  (lm_readreg(sc, WB_BANK2_T3L) & 0x80) >> 7;
+	DPRINTF(("sdata[%d] 0x%x\n", 11, sdata));
+	sc->sensors[11].cur.data_us = (sdata * 1000000) / 2 + 273150000;
+
+	/* Fans */
+	lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+	for (i = 12; i < 15; i++) {
+		sdata = lm_readreg(sc, LMD_SENSORBASE + i - 4);
+		if (i == 12)
+			divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) & 0x3;
+		else if (i == 13)
+			divisor = (lm_readreg(sc, LMD_VIDFAN) >> 6) & 0x3;
+		else
+			divisor = (lm_readreg(sc, WB_PIN) >> 6) & 0x3;
+		divisor |= (lm_readreg(sc, WB_BANK0_FANBAT) >> (i - 9)) & 0x4;
+
+		DPRINTF(("sdata[%d] 0x%x div 0x%x\n", i, sdata, divisor));
+		if (sdata == 0xff || sdata == 0x00) {
+			sc->sensors[i].cur.data_us = 0;
+		} else {
+			sc->sensors[i].cur.data_us = 1350000 /
+			    (sdata << divisor);
 		}
 	}
 }
Index: nslm7xvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/nslm7xvar.h,v
retrieving revision 1.3
diff -u -b -r1.3 nslm7xvar.h
--- nslm7xvar.h	2000/06/24 00:37:19	1.3
+++ nslm7xvar.h	2000/07/24 13:06:41
@@ -1,4 +1,4 @@
-/*	$NetBSD: nslm7xvar.h,v 1.3 2000/06/24 00:37:19 thorpej Exp $ */
+/*	$NetBSD: nslm7xvar.h,v 1.2 2000/03/07 18:39:14 groo Exp $ */
 
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -67,6 +67,40 @@
 #define LM_ID_LM79	0xC0
 #define LM_ID_MASK	0xFE
 
+/* additionnal registers for the Winbond W83627HF */
+#define WB_PIN		0x4B	/* pin & fan3 divider */
+#define WB_BANKSEL	0x4E	/* banck select register */
+#define WB_BANKSEL_B0	0x00	/* select bank 0 */
+#define WB_BANKSEL_B1	0x01	/* select bank 1 */
+#define WB_BANKSEL_B2	0x02	/* select bank 2 */
+#define WB_BANKSEL_B3	0x03	/* select bank 3 */
+#define WB_BANKSEL_B4	0x04	/* select bank 4 */
+#define WB_BANKSEL_B5	0x05	/* select bank 5 */
+#define WB_BANKSEL_HBAC	0x80	/* hight byte access */
+
+#define WB_VENDID	0x4F	/* vendor ID register */
+#define WB_VENDID_WINBOND 0x5CA3
+/* Bank0 regs */
+#define WB_BANK0_FANBAT	0x5D
+/* Bank1 regs */
+#define WB_BANK1_T2H	0x50
+#define WB_BANK1_T2L	0x51
+
+/* Bank2 regs */
+#define WB_BANK2_T3H	0x50
+#define WB_BANK2_T3L	0x51
+
+/* Bank4 regs */
+#define WB_BANK4_T1OFF	0x54
+#define WB_BANK4_T2OFF	0x55
+#define WB_BANK4_T3OFF	0x56
+
+/* Bank5 regs */
+#define WB_BANK5_5VSB	0x50
+#define WB_BANK5_VBAT	0x51
+
+#define WB_NUM_SENSORS	15
+
 struct lm_softc {
 	struct	device sc_dev;
 
@@ -76,8 +110,10 @@
 
 	int	sc_flags;
 	struct	timeval lastread; /* only allow reads every 1.5 seconds */
-	struct	envsys_tre_data sensors[LM_NUM_SENSORS];
-	struct	envsys_basic_info info[LM_NUM_SENSORS];
+	struct	envsys_tre_data sensors[WB_NUM_SENSORS];
+	struct	envsys_basic_info info[WB_NUM_SENSORS];
+	int numsensors;
+	void (*refresh_sensor_data) __P((struct lm_softc *));
 
 	struct sysmon_envsys sc_sysmon;
 };

--3MwIy2ne0vdjdPXF--