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