1 /* $NetBSD: viaenv.c,v 1.6 2002/03/25 21:29:58 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2000 Johan Danielsson 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of author nor the names of any contributors may 19 * be used to endorse or promote products derived from this 20 * software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* driver for the hardware monitoring part of the VIA VT82C686A */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: viaenv.c,v 1.6 2002/03/25 21:29:58 thorpej Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/ioctl.h> 44 #include <sys/kthread.h> 45 #include <sys/lock.h> 46 #include <sys/errno.h> 47 #include <sys/device.h> 48 49 #include <dev/pci/pcivar.h> 50 #include <dev/pci/pcireg.h> 51 52 #include <dev/pci/viapmvar.h> 53 54 #include <dev/sysmon/sysmonvar.h> 55 56 #ifdef VIAENV_DEBUG 57 unsigned int viaenv_debug = 0; 58 #define DPRINTF(X) do { if(viaenv_debug) printf X ; } while(0) 59 #else 60 #define DPRINTF(X) 61 #endif 62 63 #define VIANUMSENSORS 10 /* three temp, two fan, five voltage */ 64 65 struct viaenv_softc { 66 struct device sc_dev; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69 70 int sc_fan_div[2]; /* fan RPM divisor */ 71 72 struct envsys_tre_data sc_data[VIANUMSENSORS]; 73 struct envsys_basic_info sc_info[VIANUMSENSORS]; 74 75 struct simplelock sc_slock; 76 struct timeval sc_lastread; 77 78 struct sysmon_envsys sc_sysmon; 79 }; 80 81 const struct envsys_range viaenv_ranges[] = { 82 { 0, 2, ENVSYS_STEMP }, 83 { 3, 4, ENVSYS_SFANRPM }, 84 { 0, 1, ENVSYS_SVOLTS_AC }, /* none */ 85 { 5, 11, ENVSYS_SVOLTS_DC }, 86 { 1, 0, ENVSYS_SOHMS }, /* none */ 87 { 1, 0, ENVSYS_SWATTS }, /* none */ 88 { 1, 0, ENVSYS_SAMPS }, /* none */ 89 }; 90 91 int viaenv_gtredata(struct sysmon_envsys *, struct envsys_tre_data *); 92 int viaenv_streinfo(struct sysmon_envsys *, struct envsys_basic_info *); 93 94 static int 95 viaenv_match(struct device * parent, struct cfdata * match, void *aux); 96 static void 97 viaenv_attach(struct device * parent, struct device * self, void *aux); 98 99 struct cfattach viaenv_ca = { 100 sizeof(struct viaenv_softc), viaenv_match, viaenv_attach 101 }; 102 103 static int 104 viaenv_match(struct device * parent, struct cfdata * match, void *aux) 105 { 106 struct viapm_attach_args *va = aux; 107 108 if (va->va_type == VIAPM_HWMON) 109 return 1; 110 return 0; 111 } 112 113 /* 114 * XXX there doesn't seem to exist much hard documentation on how to 115 * convert the raw values to usable units, this code is more or less 116 * stolen from the Linux driver, but changed to suit our conditions 117 */ 118 119 /* 120 * lookup-table to translate raw values to uK, this is the same table 121 * used by the Linux driver (modulo units); there is a fifth degree 122 * polynomial that supposedly been used to generate this table, but I 123 * haven't been able to figure out how -- it doesn't give the same values 124 */ 125 126 static const long val_to_temp[] = { 127 20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955, 128 22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445, 129 23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565, 130 24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435, 131 25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115, 132 26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675, 133 26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165, 134 27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605, 135 27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025, 136 28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445, 137 28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865, 138 28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295, 139 29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725, 140 29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165, 141 30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615, 142 30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075, 143 31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545, 144 31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065, 145 32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635, 146 32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305, 147 33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115, 148 34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135, 149 35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435, 150 36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125, 151 38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325, 152 40575, 40835, 41095, 41375, 41655, 41935, 153 }; 154 155 /* use above table to convert values to temperatures in micro-Kelvins */ 156 static int 157 val_to_uK(unsigned int val) 158 { 159 int i = val / 4; 160 int j = val % 4; 161 162 assert(i >= 0 && i <= 255); 163 164 if (j == 0 || i == 255) 165 return val_to_temp[i] * 10000; 166 167 /* is linear interpolation ok? */ 168 return (val_to_temp[i] * (4 - j) + 169 val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ; 170 } 171 172 static int 173 val_to_rpm(unsigned int val, int div) 174 { 175 176 if (val == 0) 177 return 0; 178 179 return 1350000 / val / div; 180 } 181 182 static long 183 val_to_uV(unsigned int val, int index) 184 { 185 static const long mult[] = 186 {1250000, 1250000, 1670000, 2600000, 6300000}; 187 188 assert(index >= 0 && index <= 4); 189 190 return (25LL * val + 133) * mult[index] / 2628; 191 } 192 193 #define VIAENV_TSENS3 0x1f 194 #define VIAENV_TSENS1 0x20 195 #define VIAENV_TSENS2 0x21 196 #define VIAENV_VSENS1 0x22 197 #define VIAENV_VSENS2 0x23 198 #define VIAENV_VCORE 0x24 199 #define VIAENV_VSENS3 0x25 200 #define VIAENV_VSENS4 0x26 201 #define VIAENV_FAN1 0x29 202 #define VIAENV_FAN2 0x2a 203 #define VIAENV_FANCONF 0x47 /* fan configuration */ 204 #define VIAENV_TLOW 0x49 /* temperature low order value */ 205 #define VIAENV_TIRQ 0x4b /* temperature interrupt configuration */ 206 207 208 static void 209 viaenv_refresh_sensor_data(struct viaenv_softc *sc) 210 { 211 static const struct timeval onepointfive = { 1, 500000 }; 212 struct timeval t; 213 u_int8_t v, v2; 214 int i, s; 215 216 /* Read new values at most once every 1.5 seconds. */ 217 timeradd(&sc->sc_lastread, &onepointfive, &t); 218 s = splclock(); 219 i = timercmp(&mono_time, &t, >); 220 if (i) 221 sc->sc_lastread = mono_time; 222 splx(s); 223 224 if (i == 0) 225 return; 226 227 /* temperature */ 228 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ); 229 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1); 230 DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6))); 231 sc->sc_data[0].cur.data_us = val_to_uK((v2 << 2) | (v >> 6)); 232 sc->sc_data[0].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 233 234 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW); 235 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2); 236 DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3))); 237 sc->sc_data[1].cur.data_us = 238 val_to_uK((v2 << 2) | ((v >> 4) & 0x3)); 239 sc->sc_data[1].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 240 241 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3); 242 DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6))); 243 sc->sc_data[2].cur.data_us = val_to_uK((v2 << 2) | (v >> 6)); 244 sc->sc_data[2].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 245 246 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF); 247 248 sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3); 249 sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3); 250 251 /* fan */ 252 for (i = 3; i <= 4; i++) { 253 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 254 VIAENV_FAN1 + i - 3); 255 DPRINTF(("FAN%d = %d / %d\n", i - 3, v, 256 sc->sc_fan_div[i - 3])); 257 sc->sc_data[i].cur.data_us = val_to_rpm(v, 258 sc->sc_fan_div[i - 3]); 259 sc->sc_data[i].validflags = 260 ENVSYS_FVALID | ENVSYS_FCURVALID; 261 } 262 263 /* voltage */ 264 for (i = 5; i <= 9; i++) { 265 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 266 VIAENV_VSENS1 + i - 5); 267 DPRINTF(("V%d = %d\n", i - 5, v)); 268 sc->sc_data[i].cur.data_us = val_to_uV(v, i - 5); 269 sc->sc_data[i].validflags = 270 ENVSYS_FVALID | ENVSYS_FCURVALID; 271 } 272 } 273 274 static void 275 viaenv_attach(struct device * parent, struct device * self, void *aux) 276 { 277 struct viapm_attach_args *va = aux; 278 struct viaenv_softc *sc = (struct viaenv_softc *) self; 279 pcireg_t iobase, control; 280 int i; 281 282 iobase = pci_conf_read(va->va_pc, va->va_tag, va->va_offset); 283 control = pci_conf_read(va->va_pc, va->va_tag, va->va_offset + 4); 284 if ((iobase & 0xff80) == 0 || (control & 1) == 0) { 285 printf(": disabled\n"); 286 return; 287 } 288 sc->sc_iot = va->va_iot; 289 if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) { 290 printf(": failed to map i/o\n"); 291 return; 292 } 293 printf("\n"); 294 295 simple_lock_init(&sc->sc_slock); 296 297 /* Initialize sensors */ 298 for (i = 0; i < VIANUMSENSORS; ++i) { 299 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 300 sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID); 301 sc->sc_info[i].validflags = ENVSYS_FVALID; 302 sc->sc_data[i].warnflags = ENVSYS_WARN_OK; 303 } 304 305 for (i = 0; i <= 2; i++) { 306 sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP; 307 } 308 strcpy(sc->sc_info[0].desc, "TSENS1"); 309 strcpy(sc->sc_info[1].desc, "TSENS2"); 310 strcpy(sc->sc_info[2].desc, "TSENS3"); 311 312 for (i = 3; i <= 4; i++) { 313 sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM; 314 } 315 strcpy(sc->sc_info[3].desc, "FAN1"); 316 strcpy(sc->sc_info[4].desc, "FAN2"); 317 318 for (i = 5; i <= 9; ++i) { 319 sc->sc_data[i].units = sc->sc_info[i].units = 320 ENVSYS_SVOLTS_DC; 321 sc->sc_info[i].rfact = 1; /* what is this used for? */ 322 } 323 strcpy(sc->sc_info[5].desc, "VSENS1"); /* CPU core (2V) */ 324 strcpy(sc->sc_info[6].desc, "VSENS2"); /* NB core? (2.5V) */ 325 strcpy(sc->sc_info[7].desc, "Vcore"); /* Vcore (3.3V) */ 326 strcpy(sc->sc_info[8].desc, "VSENS3"); /* VSENS3 (5V) */ 327 strcpy(sc->sc_info[9].desc, "VSENS4"); /* VSENS4 (12V) */ 328 329 /* Get initial set of sensor values. */ 330 viaenv_refresh_sensor_data(sc); 331 332 /* 333 * Hook into the System Monitor. 334 */ 335 sc->sc_sysmon.sme_ranges = viaenv_ranges; 336 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 337 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 338 sc->sc_sysmon.sme_cookie = sc; 339 340 sc->sc_sysmon.sme_gtredata = viaenv_gtredata; 341 sc->sc_sysmon.sme_streinfo = viaenv_streinfo; 342 343 sc->sc_sysmon.sme_nsensors = VIANUMSENSORS; 344 sc->sc_sysmon.sme_envsys_version = 1000; 345 346 if (sysmon_envsys_register(&sc->sc_sysmon)) 347 printf("%s: unable to register with sysmon\n", 348 sc->sc_dev.dv_xname); 349 } 350 351 int 352 viaenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 353 { 354 struct viaenv_softc *sc = sme->sme_cookie; 355 356 simple_lock(&sc->sc_slock); 357 358 viaenv_refresh_sensor_data(sc); 359 *tred = sc->sc_data[tred->sensor]; 360 361 simple_unlock(&sc->sc_slock); 362 363 return (0); 364 } 365 366 int 367 viaenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 368 { 369 370 /* XXX Not implemented */ 371 binfo->validflags = 0; 372 373 return (0); 374 } 375