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