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