Home | History | Annotate | Line # | Download | only in ic
nslm7x.c revision 1.1
      1 /*	$NetBSD: nslm7x.c,v 1.1 2000/02/25 02:17:43 groo 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 <sys/envsys.h>
     53 
     54 #include <machine/bus.h>
     55 
     56 #include <dev/isa/isareg.h>
     57 #include <dev/isa/isavar.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 struct envsys_range 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 #define SCFLAG_OREAD	0x00000001
     83 #define SCFLAG_OWRITE	0x00000002
     84 #define SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
     85 
     86 u_int8_t lm_readreg __P((struct lm_softc *, int));
     87 void lm_writereg __P((struct lm_softc *, int, int));
     88 void lm_refresh_sensor_data __P((struct lm_softc *));
     89 
     90 cdev_decl(lm);
     91 
     92 extern struct cfdriver lm_cd;
     93 
     94 #define LMUNIT(x)	(minor(x))
     95 
     96 u_int8_t
     97 lm_readreg(sc, reg)
     98 	struct lm_softc *sc;
     99 	int reg;
    100 {
    101 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_ADDR, reg);
    102 	return (bus_space_read_1(sc->lm_iot, sc->lm_ioh, LMC_DATA));
    103 }
    104 
    105 void
    106 lm_writereg(sc, reg, val)
    107 	struct lm_softc *sc;
    108 	int reg;
    109 	int val;
    110 {
    111 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_ADDR, reg);
    112 	bus_space_write_1(sc->lm_iot, sc->lm_ioh, LMC_DATA, val);
    113 }
    114 
    115 
    116 /*
    117  * pre:  lmsc contains valid busspace tag and handle
    118  */
    119 void
    120 lm_attach(lmsc)
    121 	struct lm_softc *lmsc;
    122 {
    123 	int i;
    124 
    125 	/* See if we have an LM78 or LM79 */
    126 	i = lm_readreg(lmsc, LMD_CHIPID) & LM_ID_MASK;
    127 	printf(": LM7");
    128 	if (i == LM_ID_LM78)
    129 		printf("8\n");
    130 	else if (i == LM_ID_LM78J)
    131 		printf("8J\n");
    132 	else if (i == LM_ID_LM79)
    133 		printf("9\n");
    134 	else
    135 		printf("? - Unknown chip ID (%x)\n", i);
    136 
    137 	/* Start the monitoring loop */
    138 	lm_writereg(lmsc, LMD_CONFIG, 0x01);
    139 
    140 	/* Indicate we have never read the registers */
    141 	timerclear(&lmsc->lastread);
    142 
    143 	/* Initialize sensors */
    144 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    145 		lmsc->sensors[i].sensor = lmsc->info[i].sensor = i;
    146 		lmsc->sensors[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
    147 		lmsc->info[i].validflags = ENVSYS_FVALID;
    148 		lmsc->sensors[i].warnflags = ENVSYS_WARN_OK;
    149 	}
    150 
    151 	for (i = 0; i < 7; ++i) {
    152 		lmsc->sensors[i].units = lmsc->info[i].units =
    153 		    ENVSYS_SVOLTS_DC;
    154 
    155 		lmsc->info[i].desc[0] = 'I';
    156 		lmsc->info[i].desc[1] = 'N';
    157 		lmsc->info[i].desc[2] = i + '0';
    158 		lmsc->info[i].desc[3] = 0;
    159 	}
    160 
    161 	lmsc->sensors[7].units = ENVSYS_STEMP;
    162 	strcpy(lmsc->info[7].desc, "MB Temp");
    163 
    164 	for (i = 8; i < 11; ++i) {
    165 		lmsc->sensors[i].units = lmsc->info[i].units = ENVSYS_SFANRPM;
    166 
    167 		lmsc->info[i].desc[0] = 'F';
    168 		lmsc->info[i].desc[1] = 'a';
    169 		lmsc->info[i].desc[2] = 'n';
    170 		lmsc->info[i].desc[3] = ' ';
    171 		lmsc->info[i].desc[4] = i - 7 + '0';
    172 		lmsc->info[i].desc[5] = 0;
    173 	}
    174 }
    175 
    176 
    177 int
    178 lmopen(dev, flag, mode, p)
    179 	dev_t dev;
    180 	int flag, mode;
    181 	struct proc *p;
    182 {
    183 	int unit = LMUNIT(dev);
    184 	struct lm_softc *sc;
    185 
    186 	if (unit >= lm_cd.cd_ndevs)
    187 		return (ENXIO);
    188 	sc = lm_cd.cd_devs[unit];
    189 	if (sc == 0)
    190 		return (ENXIO);
    191 
    192 	/* XXX - add spinlocks instead! */
    193 	if (sc->sc_flags & SCFLAG_OPEN)
    194 		return (EBUSY);
    195 
    196 	sc->sc_flags |= SCFLAG_OPEN;
    197 
    198 	return 0;
    199 }
    200 
    201 
    202 int
    203 lmclose(dev, flag, mode, p)
    204 	dev_t dev;
    205 	int flag, mode;
    206 	struct proc *p;
    207 {
    208 	struct lm_softc *sc = lm_cd.cd_devs[LMUNIT(dev)];
    209 
    210 	DPRINTF(("lmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
    211 
    212 	sc->sc_flags &= ~SCFLAG_OPEN;
    213 
    214 	return 0;
    215 }
    216 
    217 
    218 int
    219 lmioctl(dev, cmd, data, flag, p)
    220 	dev_t dev;
    221 	u_long cmd;
    222 	caddr_t data;
    223 	int flag;
    224 	struct proc *p;
    225 {
    226 	struct lm_softc *sc = lm_cd.cd_devs[LMUNIT(dev)];
    227 	struct envsys_range *rng;
    228 	struct envsys_tre_data *tred;
    229 	struct envsys_basic_info *binfo;
    230 	struct timeval t, onepointfive = { 1, 500000 };
    231 	u_int8_t sdata;
    232 	int32_t *vers;
    233 	int i, s;
    234 	int divisor;
    235 
    236 	switch (cmd) {
    237 	case ENVSYS_VERSION:
    238 		vers = (int32_t *)data;
    239 		*vers = 1000;
    240 
    241 		return (0);
    242 
    243 	case ENVSYS_GRANGE:
    244 		rng = (struct envsys_range *)data;
    245 		if ((rng->units < ENVSYS_STEMP) ||
    246 		    (rng->units > ENVSYS_SAMPS) ) {
    247 			/* Return empty range for unsupp sensor types */
    248 			rng->low = 1;
    249 			rng->high = 0;
    250 		} else {
    251 			rng->low  = ranges[rng->units].low;
    252 			rng->high = ranges[rng->units].high;
    253 		}
    254 
    255 		return (0);
    256 
    257 	case ENVSYS_GTREDATA:
    258 		tred = (struct envsys_tre_data *)data;
    259 		tred->validflags = 0;
    260 
    261 		if (tred->sensor < LM_NUM_SENSORS) {
    262 			/* read new values at most once every 1.5 seconds */
    263 			s = splclock();
    264 
    265 			timeradd(&sc->lastread, &onepointfive, &t);
    266 
    267 			i = timercmp(&mono_time, &t, >);
    268 			if (i) {
    269 				sc->lastread.tv_sec  = mono_time.tv_sec;
    270 				sc->lastread.tv_usec = mono_time.tv_usec;
    271 			}
    272 			splx(s);
    273 
    274 			if (i) {
    275 				lm_refresh_sensor_data(sc);
    276 			}
    277 
    278 			bcopy(&sc->sensors[tred->sensor], tred,
    279 			      sizeof(struct envsys_tre_data));
    280 		}
    281 
    282 		return (0);
    283 
    284 	case ENVSYS_GTREINFO:
    285 		binfo = (struct envsys_basic_info *)data;
    286 
    287 		if (binfo->sensor >= LM_NUM_SENSORS)
    288 			binfo->validflags = 0;
    289 		else
    290 			bcopy(&sc->info[binfo->sensor], binfo,
    291 			      sizeof(struct envsys_basic_info));
    292 
    293 		return (0);
    294 
    295 	case ENVSYS_STREINFO:
    296 		binfo = (struct envsys_basic_info *)data;
    297 
    298 		if (binfo->sensor >= LM_NUM_SENSORS)
    299 			binfo->validflags = 0;
    300 		else {
    301 			/* FAN1 and FAN2 can have divisors set, but not FAN3 */
    302 			if ((sc->info[binfo->sensor].units == ENVSYS_SFANRPM)
    303 			    && (binfo->sensor != 10)) {
    304 
    305 				if (binfo->rpms == 0) {
    306 					binfo->validflags = 0;
    307 					return (0);
    308 				}
    309 
    310 				/* 153 is the nominal FAN speed value */
    311 				divisor = 1350000 / (binfo->rpms * 153);
    312 
    313 				/* ...but we need lg(divisor) */
    314 				if (divisor <= 1)
    315 					divisor = 0;
    316 				else if (divisor <= 2)
    317 					divisor = 1;
    318 				else if (divisor <= 4)
    319 					divisor = 2;
    320 				else
    321 					divisor = 3;
    322 
    323 				/*
    324 				 * FAN1 div is in bits <5:4>, FAN2 div is
    325 				 * in <7:6>
    326 				 */
    327 				sdata = lm_readreg(sc, LMD_VIDFAN);
    328 				if ( binfo->sensor == 8 ) {  /* FAN1 */
    329 					divisor <<= 4;
    330 					sdata = (sdata & 0xCF) | divisor;
    331 				} else { /* FAN2 */
    332 					divisor <<= 6;
    333 					sdata = (sdata & 0x3F) | divisor;
    334 				}
    335 
    336 				lm_writereg(sc, LMD_VIDFAN, sdata);
    337 			}
    338 
    339 			bcopy(binfo->desc, sc->info[binfo->sensor].desc, 33);
    340 			sc->info[binfo->sensor].desc[32] = 0;
    341 
    342 			binfo->validflags = ENVSYS_FVALID;
    343 		}
    344 
    345 		return (0);
    346 
    347 	default:
    348 		return (ENOTTY);
    349 	}
    350 }
    351 
    352 
    353 /*
    354  * pre:  last read occured >= 1.5 seconds ago
    355  * post: sensors[] current data are the latest from the chip
    356  */
    357 void
    358 lm_refresh_sensor_data(sc)
    359 	struct lm_softc *sc;
    360 {
    361 	u_int8_t sdata;
    362 	int i, divisor;
    363 
    364 	/* Refresh our stored data for every sensor */
    365 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    366 		sdata = lm_readreg(sc, LMD_SENSORBASE + i);
    367 
    368 		switch (sc->sensors[i].units) {
    369 		case ENVSYS_STEMP:
    370 			/* temp is given in deg. C, we convert to uK */
    371 			sc->sensors[i].cur.data_us = sdata * 1000000 +
    372 			    273150000;
    373 			break;
    374 
    375 		case ENVSYS_SVOLTS_DC:
    376 			/* voltage returned as (mV >> 4), we convert to uVDC */
    377 			sc->sensors[i].cur.data_s = (sdata << 4) * 1000;
    378 
    379 			/* these two are negative voltages */
    380 			if ( (i == 5) || (i == 6) )
    381 				sc->sensors[i].cur.data_s *= -1;
    382 
    383 			/*
    384 			 * XXX - Motherboard manufacturers can wire up whatever
    385 			 * resistors they want!  These values may have been
    386 			 * attenuated
    387 			 */
    388 
    389 			break;
    390 
    391 		case ENVSYS_SFANRPM:
    392 			if (i == 10)
    393 				divisor = 2;	/* Fixed divisor for FAN3 */
    394 			else if (i == 9)	/* Bits 7 & 6 of VID/FAN  */
    395 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 6) &
    396 				    0x3;
    397 			else
    398 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) &
    399 				    0x3;
    400 
    401 			sc->sensors[i].cur.data_us = 1350000 /
    402 			    (sdata << divisor);
    403 
    404 			break;
    405 
    406 		default:
    407 			/* XXX - debug log something? */
    408 			sc->sensors[i].validflags = 0;
    409 
    410 			break;
    411 		}
    412 	}
    413 }
    414