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