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