Home | History | Annotate | Line # | Download | only in ic
nslm7x.c revision 1.3
      1 /*	$NetBSD: nslm7x.c,v 1.3 2000/03/09 04:20:58 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  * bus independent probe
    118  */
    119 int
    120 lm_probe(iot, ioh)
    121 	bus_space_tag_t iot;
    122 	bus_space_handle_t ioh;
    123 {
    124 	u_int8_t cr;
    125 	int rv;
    126 
    127 	/* Check for some power-on defaults */
    128 	bus_space_write_1(iot, ioh, LMC_ADDR, LMD_CONFIG);
    129 
    130 	/* Perform LM78 reset */
    131 	bus_space_write_1(iot, ioh, LMC_DATA, 0x80);
    132 
    133 	/* XXX - Why do I have to reselect the register? */
    134 	bus_space_write_1(iot, ioh, LMC_ADDR, LMD_CONFIG);
    135 	cr = bus_space_read_1(iot, ioh, LMC_DATA);
    136 
    137 	/* XXX - spec says *only* 0x08! */
    138 	if ((cr == 0x08) || (cr == 0x01))
    139 		rv = 1;
    140 	else
    141 		rv = 0;
    142 
    143 	DPRINTF(("lm: rv = %d, cr = %x\n", rv, cr));
    144 
    145 	return (rv);
    146 }
    147 
    148 
    149 /*
    150  * pre:  lmsc contains valid busspace tag and handle
    151  */
    152 void
    153 lm_attach(lmsc)
    154 	struct lm_softc *lmsc;
    155 {
    156 	int i;
    157 
    158 	/* See if we have an LM78 or LM79 */
    159 	i = lm_readreg(lmsc, LMD_CHIPID) & LM_ID_MASK;
    160 	printf(": LM7");
    161 	if (i == LM_ID_LM78)
    162 		printf("8\n");
    163 	else if (i == LM_ID_LM78J)
    164 		printf("8J\n");
    165 	else if (i == LM_ID_LM79)
    166 		printf("9\n");
    167 	else
    168 		printf("? - Unknown chip ID (%x)\n", i);
    169 
    170 	/* Start the monitoring loop */
    171 	lm_writereg(lmsc, LMD_CONFIG, 0x01);
    172 
    173 	/* Indicate we have never read the registers */
    174 	timerclear(&lmsc->lastread);
    175 
    176 	/* Initialize sensors */
    177 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    178 		lmsc->sensors[i].sensor = lmsc->info[i].sensor = i;
    179 		lmsc->sensors[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
    180 		lmsc->info[i].validflags = ENVSYS_FVALID;
    181 		lmsc->sensors[i].warnflags = ENVSYS_WARN_OK;
    182 	}
    183 
    184 	for (i = 0; i < 7; ++i) {
    185 		lmsc->sensors[i].units = lmsc->info[i].units =
    186 		    ENVSYS_SVOLTS_DC;
    187 
    188 		lmsc->info[i].desc[0] = 'I';
    189 		lmsc->info[i].desc[1] = 'N';
    190 		lmsc->info[i].desc[2] = i + '0';
    191 		lmsc->info[i].desc[3] = 0;
    192 	}
    193 
    194 	/* default correction factors for resistors on higher voltage inputs */
    195 	lmsc->info[0].rfact = lmsc->info[1].rfact =
    196 	    lmsc->info[2].rfact = 10000;
    197 	lmsc->info[3].rfact = (int)(( 90.9 / 60.4) * 10000);
    198 	lmsc->info[4].rfact = (int)(( 38.0 / 10.0) * 10000);
    199 	lmsc->info[5].rfact = (int)((210.0 / 60.4) * 10000);
    200 	lmsc->info[6].rfact = (int)(( 90.9 / 60.4) * 10000);
    201 
    202 	lmsc->sensors[7].units = ENVSYS_STEMP;
    203 	strcpy(lmsc->info[7].desc, "Temp");
    204 
    205 	for (i = 8; i < 11; ++i) {
    206 		lmsc->sensors[i].units = lmsc->info[i].units = ENVSYS_SFANRPM;
    207 
    208 		lmsc->info[i].desc[0] = 'F';
    209 		lmsc->info[i].desc[1] = 'a';
    210 		lmsc->info[i].desc[2] = 'n';
    211 		lmsc->info[i].desc[3] = ' ';
    212 		lmsc->info[i].desc[4] = i - 7 + '0';
    213 		lmsc->info[i].desc[5] = 0;
    214 	}
    215 }
    216 
    217 
    218 int
    219 lmopen(dev, flag, mode, p)
    220 	dev_t dev;
    221 	int flag, mode;
    222 	struct proc *p;
    223 {
    224 	int unit = LMUNIT(dev);
    225 	struct lm_softc *sc;
    226 
    227 	if (unit >= lm_cd.cd_ndevs)
    228 		return (ENXIO);
    229 	sc = lm_cd.cd_devs[unit];
    230 	if (sc == 0)
    231 		return (ENXIO);
    232 
    233 	/* XXX - add spinlocks instead! */
    234 	if (sc->sc_flags & SCFLAG_OPEN)
    235 		return (EBUSY);
    236 
    237 	sc->sc_flags |= SCFLAG_OPEN;
    238 
    239 	return 0;
    240 }
    241 
    242 
    243 int
    244 lmclose(dev, flag, mode, p)
    245 	dev_t dev;
    246 	int flag, mode;
    247 	struct proc *p;
    248 {
    249 	struct lm_softc *sc = lm_cd.cd_devs[LMUNIT(dev)];
    250 
    251 	DPRINTF(("lmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
    252 
    253 	sc->sc_flags &= ~SCFLAG_OPEN;
    254 
    255 	return 0;
    256 }
    257 
    258 
    259 int
    260 lmioctl(dev, cmd, data, flag, p)
    261 	dev_t dev;
    262 	u_long cmd;
    263 	caddr_t data;
    264 	int flag;
    265 	struct proc *p;
    266 {
    267 	struct lm_softc *sc = lm_cd.cd_devs[LMUNIT(dev)];
    268 	struct envsys_range *rng;
    269 	struct envsys_tre_data *tred;
    270 	struct envsys_basic_info *binfo;
    271 	struct timeval t, onepointfive = { 1, 500000 };
    272 	u_int8_t sdata;
    273 	int32_t *vers;
    274 	int i, s;
    275 	int divisor;
    276 
    277 	switch (cmd) {
    278 	case ENVSYS_VERSION:
    279 		vers = (int32_t *)data;
    280 		*vers = 1000;
    281 
    282 		return (0);
    283 
    284 	case ENVSYS_GRANGE:
    285 		rng = (struct envsys_range *)data;
    286 		if ((rng->units < ENVSYS_STEMP) ||
    287 		    (rng->units > ENVSYS_SAMPS) ) {
    288 			/* Return empty range for unsupp sensor types */
    289 			rng->low = 1;
    290 			rng->high = 0;
    291 		} else {
    292 			rng->low  = ranges[rng->units].low;
    293 			rng->high = ranges[rng->units].high;
    294 		}
    295 
    296 		return (0);
    297 
    298 	case ENVSYS_GTREDATA:
    299 		tred = (struct envsys_tre_data *)data;
    300 		tred->validflags = 0;
    301 
    302 		if (tred->sensor < LM_NUM_SENSORS) {
    303 			/* read new values at most once every 1.5 seconds */
    304 			s = splclock();
    305 
    306 			timeradd(&sc->lastread, &onepointfive, &t);
    307 
    308 			i = timercmp(&mono_time, &t, >);
    309 			if (i) {
    310 				sc->lastread.tv_sec  = mono_time.tv_sec;
    311 				sc->lastread.tv_usec = mono_time.tv_usec;
    312 			}
    313 			splx(s);
    314 
    315 			if (i) {
    316 				lm_refresh_sensor_data(sc);
    317 			}
    318 
    319 			bcopy(&sc->sensors[tred->sensor], tred,
    320 			      sizeof(struct envsys_tre_data));
    321 		}
    322 
    323 		return (0);
    324 
    325 	case ENVSYS_GTREINFO:
    326 		binfo = (struct envsys_basic_info *)data;
    327 
    328 		if (binfo->sensor >= LM_NUM_SENSORS)
    329 			binfo->validflags = 0;
    330 		else
    331 			bcopy(&sc->info[binfo->sensor], binfo,
    332 			      sizeof(struct envsys_basic_info));
    333 
    334 		return (0);
    335 
    336 	case ENVSYS_STREINFO:
    337 		binfo = (struct envsys_basic_info *)data;
    338 
    339 		if (binfo->sensor >= LM_NUM_SENSORS)
    340 			binfo->validflags = 0;
    341 		else if (sc->info[binfo->sensor].units == ENVSYS_SVOLTS_DC)
    342 			sc->info[binfo->sensor].rfact = binfo->rfact;
    343 		else {
    344 			/* FAN1 and FAN2 can have divisors set, but not FAN3 */
    345 			if ((sc->info[binfo->sensor].units == ENVSYS_SFANRPM)
    346 			    && (binfo->sensor != 10)) {
    347 
    348 				if (binfo->rpms == 0) {
    349 					binfo->validflags = 0;
    350 					return (0);
    351 				}
    352 
    353 				/* 153 is the nominal FAN speed value */
    354 				divisor = 1350000 / (binfo->rpms * 153);
    355 
    356 				/* ...but we need lg(divisor) */
    357 				if (divisor <= 1)
    358 					divisor = 0;
    359 				else if (divisor <= 2)
    360 					divisor = 1;
    361 				else if (divisor <= 4)
    362 					divisor = 2;
    363 				else
    364 					divisor = 3;
    365 
    366 				/*
    367 				 * FAN1 div is in bits <5:4>, FAN2 div is
    368 				 * in <7:6>
    369 				 */
    370 				sdata = lm_readreg(sc, LMD_VIDFAN);
    371 				if ( binfo->sensor == 8 ) {  /* FAN1 */
    372 					divisor <<= 4;
    373 					sdata = (sdata & 0xCF) | divisor;
    374 				} else { /* FAN2 */
    375 					divisor <<= 6;
    376 					sdata = (sdata & 0x3F) | divisor;
    377 				}
    378 
    379 				lm_writereg(sc, LMD_VIDFAN, sdata);
    380 			}
    381 
    382 			bcopy(binfo->desc, sc->info[binfo->sensor].desc, 33);
    383 			sc->info[binfo->sensor].desc[32] = 0;
    384 
    385 			binfo->validflags = ENVSYS_FVALID;
    386 		}
    387 
    388 		return (0);
    389 
    390 	default:
    391 		return (ENOTTY);
    392 	}
    393 }
    394 
    395 
    396 /*
    397  * pre:  last read occured >= 1.5 seconds ago
    398  * post: sensors[] current data are the latest from the chip
    399  */
    400 void
    401 lm_refresh_sensor_data(sc)
    402 	struct lm_softc *sc;
    403 {
    404 	u_int8_t sdata;
    405 	int i, divisor;
    406 
    407 	/* Refresh our stored data for every sensor */
    408 	for (i = 0; i < LM_NUM_SENSORS; ++i) {
    409 		sdata = lm_readreg(sc, LMD_SENSORBASE + i);
    410 
    411 		switch (sc->sensors[i].units) {
    412 		case ENVSYS_STEMP:
    413 			/* temp is given in deg. C, we convert to uK */
    414 			sc->sensors[i].cur.data_us = sdata * 1000000 +
    415 			    273150000;
    416 			break;
    417 
    418 		case ENVSYS_SVOLTS_DC:
    419 			/* voltage returned as (mV >> 4), we convert to uVDC */
    420 			sc->sensors[i].cur.data_s = (sdata << 4);
    421 			/* rfact is (factor * 10^4) */
    422 			sc->sensors[i].cur.data_s *= sc->info[i].rfact;
    423 			/* division by 10 gets us back to uVDC */
    424 			sc->sensors[i].cur.data_s /= 10;
    425 
    426 			/* these two are negative voltages */
    427 			if ( (i == 5) || (i == 6) )
    428 				sc->sensors[i].cur.data_s *= -1;
    429 
    430 			break;
    431 
    432 		case ENVSYS_SFANRPM:
    433 			if (i == 10)
    434 				divisor = 2;	/* Fixed divisor for FAN3 */
    435 			else if (i == 9)	/* Bits 7 & 6 of VID/FAN  */
    436 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 6) &
    437 				    0x3;
    438 			else
    439 				divisor = (lm_readreg(sc, LMD_VIDFAN) >> 4) &
    440 				    0x3;
    441 
    442 			sc->sensors[i].cur.data_us = 1350000 /
    443 			    (sdata << divisor);
    444 
    445 			break;
    446 
    447 		default:
    448 			/* XXX - debug log something? */
    449 			sc->sensors[i].validflags = 0;
    450 
    451 			break;
    452 		}
    453 	}
    454 }
    455