Home | History | Annotate | Line # | Download | only in ic
nslm7x.c revision 1.4
      1 /*	$NetBSD: nslm7x.c,v 1.4 2000/06/24 00:37:19 thorpej Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Bill Squier.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/param.h>
     40 #include <sys/systm.h>
     41 #include <sys/kernel.h>
     42 #include <sys/proc.h>
     43 #include <sys/device.h>
     44 #include <sys/malloc.h>
     45 #include <sys/errno.h>
     46 #include <sys/queue.h>
     47 #include <sys/lock.h>
     48 #include <sys/ioctl.h>
     49 #include <sys/conf.h>
     50 #include <sys/time.h>
     51 
     52 #include <machine/bus.h>
     53 
     54 #include <dev/isa/isareg.h>
     55 #include <dev/isa/isavar.h>
     56 
     57 #include <dev/sysmon/sysmonvar.h>
     58 
     59 #include <dev/ic/nslm7xvar.h>
     60 
     61 #include <machine/intr.h>
     62 #include <machine/bus.h>
     63 
     64 #if defined(LMDEBUG)
     65 #define DPRINTF(x)		do { printf x; } while (0)
     66 #else
     67 #define DPRINTF(x)
     68 #endif
     69 
     70 const struct envsys_range lm_ranges[] = {	/* sc->sensors sub-intervals */
     71 						/* for each unit type        */
     72 	{ 7, 7,    ENVSYS_STEMP   },
     73 	{ 8, 10,   ENVSYS_SFANRPM },
     74 	{ 1, 0,    ENVSYS_SVOLTS_AC },	/* None */
     75 	{ 0, 6,    ENVSYS_SVOLTS_DC },
     76 	{ 1, 0,    ENVSYS_SOHMS },	/* None */
     77 	{ 1, 0,    ENVSYS_SWATTS },	/* None */
     78 	{ 1, 0,    ENVSYS_SAMPS }	/* None */
     79 };
     80 
     81 
     82 u_int8_t lm_readreg __P((struct lm_softc *, int));
     83 void lm_writereg __P((struct lm_softc *, int, int));
     84 void lm_refresh_sensor_data __P((struct lm_softc *));
     85 int lm_gtredata __P((struct sysmon_envsys *, struct envsys_tre_data *));
     86 int lm_streinfo __P((struct sysmon_envsys *, struct envsys_basic_info *));
     87 
     88 u_int8_t
     89 lm_readreg(sc, reg)
     90 	struct lm_softc *sc;
     91 	int reg;
     92 {
     93 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_ADDR, reg);
     94 	return (bus_space_read_1(sc->lm_iot, sc->lm_ioh, LMC_DATA));
     95 }
     96 
     97 void
     98 lm_writereg(sc, reg, val)
     99 	struct lm_softc *sc;
    100 	int reg;
    101 	int val;
    102 {
    103 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_ADDR, reg);
    104 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_DATA, val);
    105 }
    106 
    107 
    108 /*
    109  * bus independent probe
    110  */
    111 int
    112 lm_probe(iot, ioh)
    113 	bus_space_tag_t iot;
    114 	bus_space_handle_t ioh;
    115 {
    116 	u_int8_t cr;
    117 	int rv;
    118 
    119 	/* Check for some power-on defaults */
    120 	bus_space_write_1(iot, ioh, LMC_ADDR, LMD_CONFIG);
    121 
    122 	/* Perform LM78 reset */
    123 	bus_space_write_1(iot, ioh, LMC_DATA, 0x80);
    124 
    125 	/* XXX - Why do I have to reselect the register? */
    126 	bus_space_write_1(iot, ioh, LMC_ADDR, LMD_CONFIG);
    127 	cr = bus_space_read_1(iot, ioh, LMC_DATA);
    128 
    129 	/* XXX - spec says *only* 0x08! */
    130 	if ((cr == 0x08) || (cr == 0x01))
    131 		rv = 1;
    132 	else
    133 		rv = 0;
    134 
    135 	DPRINTF(("lm: rv = %d, cr = %x\n", rv, cr));
    136 
    137 	return (rv);
    138 }
    139 
    140 
    141 /*
    142  * pre:  lmsc contains valid busspace tag and handle
    143  */
    144 void
    145 lm_attach(lmsc)
    146 	struct lm_softc *lmsc;
    147 {
    148 	int i;
    149 
    150 	/* See if we have an LM78 or LM79 */
    151 	i = lm_readreg(lmsc, LMD_CHIPID) & LM_ID_MASK;
    152 	printf(": LM7");
    153 	if (i == LM_ID_LM78)
    154 		printf("8\n");
    155 	else if (i == LM_ID_LM78J)
    156 		printf("8J\n");
    157 	else if (i == LM_ID_LM79)
    158 		printf("9\n");
    159 	else
    160 		printf("? - Unknown chip ID (%x)\n", i);
    161 
    162 	/* Start the monitoring loop */
    163 	lm_writereg(lmsc, LMD_CONFIG, 0x01);
    164 
    165 	/* Indicate we have never read the registers */
    166 	timerclear(&lmsc->lastread);
    167 
    168 	/* Initialize sensors */
    169 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    170 		lmsc->sensors[i].sensor = lmsc->info[i].sensor = i;
    171 		lmsc->sensors[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
    172 		lmsc->info[i].validflags = ENVSYS_FVALID;
    173 		lmsc->sensors[i].warnflags = ENVSYS_WARN_OK;
    174 	}
    175 
    176 	for (i = 0; i < 7; ++i) {
    177 		lmsc->sensors[i].units = lmsc->info[i].units =
    178 		    ENVSYS_SVOLTS_DC;
    179 
    180 		lmsc->info[i].desc[0] = 'I';
    181 		lmsc->info[i].desc[1] = 'N';
    182 		lmsc->info[i].desc[2] = i + '0';
    183 		lmsc->info[i].desc[3] = 0;
    184 	}
    185 
    186 	/* default correction factors for resistors on higher voltage inputs */
    187 	lmsc->info[0].rfact = lmsc->info[1].rfact =
    188 	    lmsc->info[2].rfact = 10000;
    189 	lmsc->info[3].rfact = (int)(( 90.9 / 60.4) * 10000);
    190 	lmsc->info[4].rfact = (int)(( 38.0 / 10.0) * 10000);
    191 	lmsc->info[5].rfact = (int)((210.0 / 60.4) * 10000);
    192 	lmsc->info[6].rfact = (int)(( 90.9 / 60.4) * 10000);
    193 
    194 	lmsc->sensors[7].units = ENVSYS_STEMP;
    195 	strcpy(lmsc->info[7].desc, "Temp");
    196 
    197 	for (i = 8; i < 11; ++i) {
    198 		lmsc->sensors[i].units = lmsc->info[i].units = ENVSYS_SFANRPM;
    199 
    200 		lmsc->info[i].desc[0] = 'F';
    201 		lmsc->info[i].desc[1] = 'a';
    202 		lmsc->info[i].desc[2] = 'n';
    203 		lmsc->info[i].desc[3] = ' ';
    204 		lmsc->info[i].desc[4] = i - 7 + '0';
    205 		lmsc->info[i].desc[5] = 0;
    206 	}
    207 
    208 	/*
    209 	 * Hook into the System Monitor.
    210 	 */
    211 	lmsc->sc_sysmon.sme_ranges = lm_ranges;
    212 	lmsc->sc_sysmon.sme_sensor_info = lmsc->info;
    213 	lmsc->sc_sysmon.sme_sensor_data = lmsc->sensors;
    214 	lmsc->sc_sysmon.sme_cookie = lmsc;
    215 
    216 	lmsc->sc_sysmon.sme_gtredata = lm_gtredata;
    217 	lmsc->sc_sysmon.sme_streinfo = lm_streinfo;
    218 
    219 	lmsc->sc_sysmon.sme_nsensors = LM_NUM_SENSORS;
    220 	lmsc->sc_sysmon.sme_envsys_version = 1000;
    221 
    222 	if (sysmon_envsys_register(&lmsc->sc_sysmon))
    223 		printf("%s: unable to register with sysmon\n",
    224 		    lmsc->sc_dev.dv_xname);
    225 }
    226 
    227 
    228 int
    229 lm_gtredata(sme, tred)
    230 	struct sysmon_envsys *sme;
    231 	struct envsys_tre_data *tred;
    232 {
    233 	static const struct timeval onepointfive = { 1, 500000 };
    234 	struct timeval t;
    235 	struct lm_softc *sc = sme->sme_cookie;
    236 	int i, s;
    237 
    238 	/* read new values at most once every 1.5 seconds */
    239 	timeradd(&sc->lastread, &onepointfive, &t);
    240 	s = splclock();
    241 	i = timercmp(&mono_time, &t, >);
    242 	if (i) {
    243 		sc->lastread.tv_sec  = mono_time.tv_sec;
    244 		sc->lastread.tv_usec = mono_time.tv_usec;
    245 	}
    246 	splx(s);
    247 
    248 	if (i)
    249 		lm_refresh_sensor_data(sc);
    250 
    251 	*tred = sc->sensors[tred->sensor];
    252 
    253 	return (0);
    254 }
    255 
    256 
    257 int
    258 lm_streinfo(sme, binfo)
    259 	struct sysmon_envsys *sme;
    260 	struct envsys_basic_info *binfo;
    261 {
    262 	struct lm_softc *sc = sme->sme_cookie;
    263 	int divisor;
    264 	u_int8_t sdata;
    265 
    266 	if (sc->info[binfo->sensor].units == ENVSYS_SVOLTS_DC)
    267 		sc->info[binfo->sensor].rfact = binfo->rfact;
    268 	else {
    269 		/* FAN1 and FAN2 can have divisors set, but not FAN3 */
    270 		if ((sc->info[binfo->sensor].units == ENVSYS_SFANRPM)
    271 		    && (binfo->sensor != 10)) {
    272 
    273 			if (binfo->rpms == 0) {
    274 				binfo->validflags = 0;
    275 				return (0);
    276 			}
    277 
    278 			/* 153 is the nominal FAN speed value */
    279 			divisor = 1350000 / (binfo->rpms * 153);
    280 
    281 			/* ...but we need lg(divisor) */
    282 			if (divisor <= 1)
    283 				divisor = 0;
    284 			else if (divisor <= 2)
    285 				divisor = 1;
    286 			else if (divisor <= 4)
    287 				divisor = 2;
    288 			else
    289 				divisor = 3;
    290 
    291 			/*
    292 			 * FAN1 div is in bits <5:4>, FAN2 div is
    293 			 * in <7:6>
    294 			 */
    295 			sdata = lm_readreg(sc, LMD_VIDFAN);
    296 			if ( binfo->sensor == 8 ) {  /* FAN1 */
    297 				divisor <<= 4;
    298 				sdata = (sdata & 0xCF) | divisor;
    299 			} else { /* FAN2 */
    300 				divisor <<= 6;
    301 				sdata = (sdata & 0x3F) | divisor;
    302 			}
    303 
    304 			lm_writereg(sc, LMD_VIDFAN, sdata);
    305 		}
    306 
    307 		memcpy(sc->info[binfo->sensor].desc, binfo->desc,
    308 		    sizeof(sc->info[binfo->sensor].desc));
    309 		sc->info[binfo->sensor].desc[
    310 		    sizeof(sc->info[binfo->sensor].desc) - 1] = '\0';
    311 
    312 		binfo->validflags = ENVSYS_FVALID;
    313 	}
    314 	return (0);
    315 }
    316 
    317 
    318 /*
    319  * pre:  last read occured >= 1.5 seconds ago
    320  * post: sensors[] current data are the latest from the chip
    321  */
    322 void
    323 lm_refresh_sensor_data(sc)
    324 	struct lm_softc *sc;
    325 {
    326 	u_int8_t sdata;
    327 	int i, divisor;
    328 
    329 	/* Refresh our stored data for every sensor */
    330 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    331 		sdata = lm_readreg(sc, LMD_SENSORBASE + i);
    332 
    333 		switch (sc->sensors[i].units) {
    334 		case ENVSYS_STEMP:
    335 			/* temp is given in deg. C, we convert to uK */
    336 			sc->sensors[i].cur.data_us = sdata * 1000000 +
    337 			    273150000;
    338 			break;
    339 
    340 		case ENVSYS_SVOLTS_DC:
    341 			/* voltage returned as (mV >> 4), we convert to uVDC */
    342 			sc->sensors[i].cur.data_s = (sdata << 4);
    343 			/* rfact is (factor * 10^4) */
    344 			sc->sensors[i].cur.data_s *= sc->info[i].rfact;
    345 			/* division by 10 gets us back to uVDC */
    346 			sc->sensors[i].cur.data_s /= 10;
    347 
    348 			/* these two are negative voltages */
    349 			if ( (i == 5) || (i == 6) )
    350 				sc->sensors[i].cur.data_s *= -1;
    351 
    352 			break;
    353 
    354 		case ENVSYS_SFANRPM:
    355 			if (i == 10)
    356 				divisor = 2;	/* Fixed divisor for FAN3 */
    357 			else if (i == 9)	/* Bits 7 & 6 of VID/FAN  */
    358 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 6) &
    359 				    0x3;
    360 			else
    361 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) &
    362 				    0x3;
    363 
    364 			sc->sensors[i].cur.data_us = 1350000 /
    365 			    (sdata << divisor);
    366 
    367 			break;
    368 
    369 		default:
    370 			/* XXX - debug log something? */
    371 			sc->sensors[i].validflags = 0;
    372 
    373 			break;
    374 		}
    375 	}
    376 }
    377