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