1 /* $NetBSD: viaenv.c,v 1.9 2002/10/02 16:51:59 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.9 2002/10/02 16:51:59 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 CFATTACH_DECL(viaenv, sizeof(struct viaenv_softc), 100 viaenv_match, viaenv_attach, NULL, NULL); 101 102 static int 103 viaenv_match(struct device * parent, struct cfdata * match, void *aux) 104 { 105 struct viapm_attach_args *va = aux; 106 107 if (va->va_type == VIAPM_HWMON) 108 return 1; 109 return 0; 110 } 111 112 /* 113 * XXX there doesn't seem to exist much hard documentation on how to 114 * convert the raw values to usable units, this code is more or less 115 * stolen from the Linux driver, but changed to suit our conditions 116 */ 117 118 /* 119 * lookup-table to translate raw values to uK, this is the same table 120 * used by the Linux driver (modulo units); there is a fifth degree 121 * polynomial that supposedly been used to generate this table, but I 122 * haven't been able to figure out how -- it doesn't give the same values 123 */ 124 125 static const long val_to_temp[] = { 126 20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955, 127 22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445, 128 23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565, 129 24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435, 130 25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115, 131 26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675, 132 26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165, 133 27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605, 134 27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025, 135 28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445, 136 28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865, 137 28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295, 138 29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725, 139 29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165, 140 30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615, 141 30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075, 142 31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545, 143 31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065, 144 32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635, 145 32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305, 146 33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115, 147 34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135, 148 35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435, 149 36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125, 150 38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325, 151 40575, 40835, 41095, 41375, 41655, 41935, 152 }; 153 154 /* use above table to convert values to temperatures in micro-Kelvins */ 155 static int 156 val_to_uK(unsigned int val) 157 { 158 int i = val / 4; 159 int j = val % 4; 160 161 assert(i >= 0 && i <= 255); 162 163 if (j == 0 || i == 255) 164 return val_to_temp[i] * 10000; 165 166 /* is linear interpolation ok? */ 167 return (val_to_temp[i] * (4 - j) + 168 val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ; 169 } 170 171 static int 172 val_to_rpm(unsigned int val, int div) 173 { 174 175 if (val == 0) 176 return 0; 177 178 return 1350000 / val / div; 179 } 180 181 static long 182 val_to_uV(unsigned int val, int index) 183 { 184 static const long mult[] = 185 {1250000, 1250000, 1670000, 2600000, 6300000}; 186 187 assert(index >= 0 && index <= 4); 188 189 return (25LL * val + 133) * mult[index] / 2628; 190 } 191 192 #define VIAENV_TSENS3 0x1f 193 #define VIAENV_TSENS1 0x20 194 #define VIAENV_TSENS2 0x21 195 #define VIAENV_VSENS1 0x22 196 #define VIAENV_VSENS2 0x23 197 #define VIAENV_VCORE 0x24 198 #define VIAENV_VSENS3 0x25 199 #define VIAENV_VSENS4 0x26 200 #define VIAENV_FAN1 0x29 201 #define VIAENV_FAN2 0x2a 202 #define VIAENV_FANCONF 0x47 /* fan configuration */ 203 #define VIAENV_TLOW 0x49 /* temperature low order value */ 204 #define VIAENV_TIRQ 0x4b /* temperature interrupt configuration */ 205 206 207 static void 208 viaenv_refresh_sensor_data(struct viaenv_softc *sc) 209 { 210 static const struct timeval onepointfive = { 1, 500000 }; 211 struct timeval t; 212 u_int8_t v, v2; 213 int i, s; 214 215 /* Read new values at most once every 1.5 seconds. */ 216 timeradd(&sc->sc_lastread, &onepointfive, &t); 217 s = splclock(); 218 i = timercmp(&mono_time, &t, >); 219 if (i) 220 sc->sc_lastread = mono_time; 221 splx(s); 222 223 if (i == 0) 224 return; 225 226 /* temperature */ 227 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TIRQ); 228 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS1); 229 DPRINTF(("TSENS1 = %d\n", (v2 << 2) | (v >> 6))); 230 sc->sc_data[0].cur.data_us = val_to_uK((v2 << 2) | (v >> 6)); 231 sc->sc_data[0].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 232 233 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TLOW); 234 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS2); 235 DPRINTF(("TSENS2 = %d\n", (v2 << 2) | ((v >> 4) & 0x3))); 236 sc->sc_data[1].cur.data_us = 237 val_to_uK((v2 << 2) | ((v >> 4) & 0x3)); 238 sc->sc_data[1].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 239 240 v2 = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_TSENS3); 241 DPRINTF(("TSENS3 = %d\n", (v2 << 2) | (v >> 6))); 242 sc->sc_data[2].cur.data_us = val_to_uK((v2 << 2) | (v >> 6)); 243 sc->sc_data[2].validflags = ENVSYS_FVALID | ENVSYS_FCURVALID; 244 245 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAENV_FANCONF); 246 247 sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3); 248 sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3); 249 250 /* fan */ 251 for (i = 3; i <= 4; i++) { 252 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 253 VIAENV_FAN1 + i - 3); 254 DPRINTF(("FAN%d = %d / %d\n", i - 3, v, 255 sc->sc_fan_div[i - 3])); 256 sc->sc_data[i].cur.data_us = val_to_rpm(v, 257 sc->sc_fan_div[i - 3]); 258 sc->sc_data[i].validflags = 259 ENVSYS_FVALID | ENVSYS_FCURVALID; 260 } 261 262 /* voltage */ 263 for (i = 5; i <= 9; i++) { 264 v = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 265 VIAENV_VSENS1 + i - 5); 266 DPRINTF(("V%d = %d\n", i - 5, v)); 267 sc->sc_data[i].cur.data_us = val_to_uV(v, i - 5); 268 sc->sc_data[i].validflags = 269 ENVSYS_FVALID | ENVSYS_FCURVALID; 270 } 271 } 272 273 static void 274 viaenv_attach(struct device * parent, struct device * self, void *aux) 275 { 276 struct viapm_attach_args *va = aux; 277 struct viaenv_softc *sc = (struct viaenv_softc *) self; 278 pcireg_t iobase, control; 279 int i; 280 281 iobase = pci_conf_read(va->va_pc, va->va_tag, va->va_offset); 282 control = pci_conf_read(va->va_pc, va->va_tag, va->va_offset + 4); 283 if ((iobase & 0xff80) == 0 || (control & 1) == 0) { 284 printf(": disabled\n"); 285 return; 286 } 287 sc->sc_iot = va->va_iot; 288 if (bus_space_map(sc->sc_iot, iobase & 0xff80, 128, 0, &sc->sc_ioh)) { 289 printf(": failed to map i/o\n"); 290 return; 291 } 292 printf("\n"); 293 294 simple_lock_init(&sc->sc_slock); 295 296 /* Initialize sensors */ 297 for (i = 0; i < VIANUMSENSORS; ++i) { 298 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 299 sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID); 300 sc->sc_info[i].validflags = ENVSYS_FVALID; 301 sc->sc_data[i].warnflags = ENVSYS_WARN_OK; 302 } 303 304 for (i = 0; i <= 2; i++) { 305 sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_STEMP; 306 } 307 strcpy(sc->sc_info[0].desc, "TSENS1"); 308 strcpy(sc->sc_info[1].desc, "TSENS2"); 309 strcpy(sc->sc_info[2].desc, "TSENS3"); 310 311 for (i = 3; i <= 4; i++) { 312 sc->sc_data[i].units = sc->sc_info[i].units = ENVSYS_SFANRPM; 313 } 314 strcpy(sc->sc_info[3].desc, "FAN1"); 315 strcpy(sc->sc_info[4].desc, "FAN2"); 316 317 for (i = 5; i <= 9; ++i) { 318 sc->sc_data[i].units = sc->sc_info[i].units = 319 ENVSYS_SVOLTS_DC; 320 sc->sc_info[i].rfact = 1; /* what is this used for? */ 321 } 322 strcpy(sc->sc_info[5].desc, "VSENS1"); /* CPU core (2V) */ 323 strcpy(sc->sc_info[6].desc, "VSENS2"); /* NB core? (2.5V) */ 324 strcpy(sc->sc_info[7].desc, "Vcore"); /* Vcore (3.3V) */ 325 strcpy(sc->sc_info[8].desc, "VSENS3"); /* VSENS3 (5V) */ 326 strcpy(sc->sc_info[9].desc, "VSENS4"); /* VSENS4 (12V) */ 327 328 /* Get initial set of sensor values. */ 329 viaenv_refresh_sensor_data(sc); 330 331 /* 332 * Hook into the System Monitor. 333 */ 334 sc->sc_sysmon.sme_ranges = viaenv_ranges; 335 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 336 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 337 sc->sc_sysmon.sme_cookie = sc; 338 339 sc->sc_sysmon.sme_gtredata = viaenv_gtredata; 340 sc->sc_sysmon.sme_streinfo = viaenv_streinfo; 341 342 sc->sc_sysmon.sme_nsensors = VIANUMSENSORS; 343 sc->sc_sysmon.sme_envsys_version = 1000; 344 345 if (sysmon_envsys_register(&sc->sc_sysmon)) 346 printf("%s: unable to register with sysmon\n", 347 sc->sc_dev.dv_xname); 348 } 349 350 int 351 viaenv_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 352 { 353 struct viaenv_softc *sc = sme->sme_cookie; 354 355 simple_lock(&sc->sc_slock); 356 357 viaenv_refresh_sensor_data(sc); 358 *tred = sc->sc_data[tred->sensor]; 359 360 simple_unlock(&sc->sc_slock); 361 362 return (0); 363 } 364 365 int 366 viaenv_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 367 { 368 369 /* XXX Not implemented */ 370 binfo->validflags = 0; 371 372 return (0); 373 } 374