1 /* $OpenBSD: viasio.c,v 1.1 2005/07/28 20:12:13 grange Exp $ */ 2 /* 3 * Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * VIA VT1211 LPC Super I/O driver. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/device.h> 25 #include <sys/kernel.h> 26 #include <sys/sensors.h> 27 #include <sys/timeout.h> 28 29 #include <machine/bus.h> 30 31 #include <dev/isa/isareg.h> 32 #include <dev/isa/isavar.h> 33 34 #include <dev/isa/viasioreg.h> 35 36 #ifdef VIASIO_DEBUG 37 #define DPRINTF(x) printf x 38 #else 39 #define DPRINTF(x) 40 #endif 41 42 /* autoconf flags */ 43 #define VIASIO_CFFLAGS_HM_ENABLE 0x0001 /* enable HM if disabled */ 44 45 struct viasio_softc { 46 struct device sc_dev; 47 48 bus_space_tag_t sc_iot; 49 bus_space_handle_t sc_ioh; 50 51 /* Hardware monitor */ 52 bus_space_handle_t sc_hm_ioh; 53 int sc_hm_clock; 54 struct sensor sc_hm_sensors[VT1211_HM_NSENSORS]; 55 struct timeout sc_hm_timo; 56 }; 57 58 int viasio_probe(struct device *, void *, void *); 59 void viasio_attach(struct device *, struct device *, void *); 60 61 void viasio_hm_init(struct viasio_softc *); 62 void viasio_hm_refresh(void *); 63 64 struct cfattach viasio_ca = { 65 sizeof(struct viasio_softc), 66 viasio_probe, 67 viasio_attach 68 }; 69 70 struct cfdriver viasio_cd = { 71 NULL, "viasio", DV_DULL 72 }; 73 74 static __inline void 75 viasio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 76 { 77 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); 78 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); 79 } 80 81 static __inline void 82 viasio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 83 { 84 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_DS_MAGIC); 85 } 86 87 static __inline u_int8_t 88 viasio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 89 { 90 bus_space_write_1(iot, ioh, VT1211_INDEX, index); 91 return (bus_space_read_1(iot, ioh, VT1211_DATA)); 92 } 93 94 static __inline void 95 viasio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 96 u_int8_t data) 97 { 98 bus_space_write_1(iot, ioh, VT1211_INDEX, index); 99 bus_space_write_1(iot, ioh, VT1211_DATA, data); 100 } 101 102 static __inline int64_t 103 viasio_raw2temp(int raw) 104 { 105 int tblsize = sizeof(vt1211_hm_temptbl) / sizeof(vt1211_hm_temptbl[0]); 106 int i; 107 int raw1, raw2; 108 int64_t temp = -1, temp1, temp2; 109 110 if (raw < vt1211_hm_temptbl[0].raw || 111 raw > vt1211_hm_temptbl[tblsize - 1].raw) 112 return (-1); 113 114 for (i = 0; i < tblsize - 1; i++) { 115 raw1 = vt1211_hm_temptbl[i].raw; 116 temp1 = vt1211_hm_temptbl[i].temp; 117 raw2 = vt1211_hm_temptbl[i + 1].raw; 118 temp2 = vt1211_hm_temptbl[i + 1].temp; 119 120 if (raw >= raw1 && raw <= raw2) { 121 /* linear interpolation */ 122 temp = temp1 + ((raw - raw1) * (temp2 - temp1)) / 123 (raw2 - raw1); 124 break; 125 } 126 } 127 128 return (temp); 129 } 130 131 int 132 viasio_probe(struct device *parent, void *match, void *aux) 133 { 134 struct isa_attach_args *ia = aux; 135 bus_space_tag_t iot; 136 bus_space_handle_t ioh; 137 u_int8_t reg; 138 139 /* Match by device ID */ 140 iot = ia->ia_iot; 141 if (bus_space_map(iot, ia->ipa_io[0].base, VT1211_IOSIZE, 0, &ioh)) 142 return (0); 143 viasio_conf_enable(iot, ioh); 144 reg = viasio_conf_read(iot, ioh, VT1211_ID); 145 DPRINTF(("viasio_probe: id 0x%02x\n", reg)); 146 viasio_conf_disable(iot, ioh); 147 bus_space_unmap(iot, ioh, VT1211_IOSIZE); 148 if (reg == VT1211_ID_VT1211) { 149 ia->ipa_nio = 1; 150 ia->ipa_io[0].length = VT1211_IOSIZE; 151 ia->ipa_nmem = 0; 152 ia->ipa_nirq = 0; 153 ia->ipa_ndrq = 0; 154 return (1); 155 } 156 157 return (0); 158 } 159 160 void 161 viasio_attach(struct device *parent, struct device *self, void *aux) 162 { 163 struct viasio_softc *sc = (void *)self; 164 struct isa_attach_args *ia = aux; 165 u_int8_t reg; 166 167 /* Map ISA I/O space */ 168 sc->sc_iot = ia->ia_iot; 169 if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, 170 VT1211_IOSIZE, 0, &sc->sc_ioh)) { 171 printf(": can't map I/O space\n"); 172 return; 173 } 174 175 /* Enter configuration mode */ 176 viasio_conf_enable(sc->sc_iot, sc->sc_ioh); 177 178 /* Read device revision */ 179 reg = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_REV); 180 printf(": VT1211 rev 0x%02x\n", reg); 181 182 /* Initialize logical devices */ 183 printf("%s", sc->sc_dev.dv_xname); 184 viasio_hm_init(sc); 185 printf("\n"); 186 187 /* Escape from configuration mode */ 188 viasio_conf_disable(sc->sc_iot, sc->sc_ioh); 189 } 190 191 void 192 viasio_hm_init(struct viasio_softc *sc) 193 { 194 u_int8_t reg0, reg1; 195 u_int16_t iobase; 196 int i; 197 198 printf(": HM"); 199 200 /* Select HM logical device */ 201 viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_HM); 202 203 /* 204 * Check if logical device is activated by firmware. If not 205 * try to activate it only if requested. 206 */ 207 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ACT); 208 DPRINTF((": ACT 0x%02x", reg0)); 209 if ((reg0 & VT1211_HM_ACT_EN) == 0) { 210 if ((sc->sc_dev.dv_cfdata->cf_flags & 211 VIASIO_CFFLAGS_HM_ENABLE) != 0) { 212 reg0 |= VT1211_HM_ACT_EN; 213 viasio_conf_write(sc->sc_iot, sc->sc_ioh, 214 VT1211_HM_ACT, reg0); 215 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, 216 VT1211_HM_ACT); 217 DPRINTF((", new ACT 0x%02x", reg0)); 218 if ((reg0 & VT1211_HM_ACT_EN) == 0) { 219 printf(": failed to activate"); 220 return; 221 } 222 } else { 223 printf(": not activated"); 224 return; 225 } 226 } 227 228 /* Read HM I/O space address */ 229 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_LSB); 230 reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_MSB); 231 iobase = (reg1 << 8) | reg0; 232 DPRINTF((", addr 0x%04x", iobase)); 233 234 /* Map HM I/O space */ 235 if (bus_space_map(sc->sc_iot, iobase, VT1211_HM_IOSIZE, 0, 236 &sc->sc_hm_ioh)) { 237 printf(": can't map I/O space"); 238 return; 239 } 240 241 /* 242 * Check if hardware monitoring is enabled by firmware. If not 243 * try to enable it only if requested. 244 */ 245 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_CONF); 246 DPRINTF((", CONF 0x%02x", reg0)); 247 if ((reg0 & VT1211_HM_CONF_START) == 0) { 248 if ((sc->sc_dev.dv_cfdata->cf_flags & 249 VIASIO_CFFLAGS_HM_ENABLE) != 0) { 250 reg0 |= VT1211_HM_CONF_START; 251 bus_space_write_1(sc->sc_iot, sc->sc_hm_ioh, 252 VT1211_HM_CONF, reg0); 253 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 254 VT1211_HM_CONF); 255 DPRINTF((", new CONF 0x%02x", reg0)); 256 if ((reg0 & VT1211_HM_CONF_START) == 0) { 257 printf(": failed to enable monitoring"); 258 return; 259 } 260 } else { 261 printf(": monitoring not enabled"); 262 return; 263 } 264 } 265 266 /* Read PWM clock frequency */ 267 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_PWMCS); 268 sc->sc_hm_clock = vt1211_hm_clock[reg0 & 0x07]; 269 DPRINTF((", PWMCS 0x%02x, %dHz", reg0, sc->sc_hm_clock)); 270 271 /* Initialize sensors */ 272 for (i = 0; i < VT1211_HM_NSENSORS; i++) 273 strlcpy(sc->sc_hm_sensors[i].device, sc->sc_dev.dv_xname, 274 sizeof(sc->sc_hm_sensors[i].device)); 275 276 /* Temperature reading 1 */ 277 sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP; 278 strlcpy(sc->sc_hm_sensors[VT1211_HMS_TEMP1].desc, "TEMP1", 279 sizeof(sc->sc_hm_sensors[VT1211_HMS_TEMP1].desc)); 280 281 /* Universal channels (UCH) 1-5 */ 282 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_UCHCONF); 283 DPRINTF((", UCHCONF 0x%02x", reg0)); 284 for (i = 1; i <= 5; i++) { 285 /* UCH can be configured either as thermal or voltage input */ 286 if (VT1211_HM_UCHCONF_ISTEMP(reg0, i)) { 287 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = 288 SENSOR_TEMP; 289 } else { 290 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = 291 SENSOR_VOLTS_DC; 292 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].rfact = 293 vt1211_hm_vrfact[i - 1]; 294 } 295 snprintf(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc, 296 sizeof(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc), 297 "UCH%d", i); 298 } 299 300 /* Internal +3.3V */ 301 sc->sc_hm_sensors[VT1211_HMS_33V].type = SENSOR_VOLTS_DC; 302 strlcpy(sc->sc_hm_sensors[VT1211_HMS_33V].desc, "+3.3V", 303 sizeof(sc->sc_hm_sensors[VT1211_HMS_33V].desc)); 304 sc->sc_hm_sensors[VT1211_HMS_33V].rfact = vt1211_hm_vrfact[5]; 305 306 /* FAN reading 1, 2 */ 307 sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM; 308 strlcpy(sc->sc_hm_sensors[VT1211_HMS_FAN1].desc, "FAN1", 309 sizeof(sc->sc_hm_sensors[VT1211_HMS_FAN1].desc)); 310 sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM; 311 strlcpy(sc->sc_hm_sensors[VT1211_HMS_FAN2].desc, "FAN2", 312 sizeof(sc->sc_hm_sensors[VT1211_HMS_FAN2].desc)); 313 314 /* Start sensors */ 315 for (i = 0; i < VT1211_HM_NSENSORS; i++) 316 SENSOR_ADD(&sc->sc_hm_sensors[i]); 317 timeout_set(&sc->sc_hm_timo, viasio_hm_refresh, sc); 318 timeout_add(&sc->sc_hm_timo, hz); 319 } 320 321 void 322 viasio_hm_refresh(void *arg) 323 { 324 struct viasio_softc *sc = arg; 325 u_int8_t reg0, reg1; 326 int64_t val, rfact; 327 int i; 328 329 /* TEMP1 is a 10-bit thermal input */ 330 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TEMP1); 331 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TCONF1); 332 reg1 = VT1211_HM_TCONF1_TEMP1(reg1); 333 val = (reg0 << 2) | reg1; 334 335 /* Convert to uK */ 336 /* XXX: conversion function is guessed */ 337 val = viasio_raw2temp(val); 338 if (val == -1) { 339 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags |= SENSOR_FINVALID; 340 } else { 341 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags &= ~SENSOR_FINVALID; 342 sc->sc_hm_sensors[VT1211_HMS_TEMP1].value = val; 343 } 344 345 /* Universal channels 1-5 */ 346 for (i = 1; i <= 5; i++) { 347 if (sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type == 348 SENSOR_TEMP) { 349 /* UCH is a 10-bit thermal input */ 350 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 351 VT1211_HM_UCH1 + i - 1); 352 if (i == 1) { 353 reg1 = bus_space_read_1(sc->sc_iot, 354 sc->sc_hm_ioh, VT1211_HM_VID4); 355 reg1 = VT1211_HM_VID4_UCH1(reg1); 356 } else { 357 reg1 = bus_space_read_1(sc->sc_iot, 358 sc->sc_hm_ioh, VT1211_HM_ETR); 359 reg1 = VT1211_HM_ETR_UCH(reg1, i); 360 } 361 val = (reg0 << 2) | reg1; 362 363 /* Convert to uK */ 364 /* XXX: conversion function is guessed */ 365 val = viasio_raw2temp(val); 366 if (val == -1) { 367 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 368 i - 1].flags |= SENSOR_FINVALID; 369 } else { 370 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 371 i - 1].flags &= ~SENSOR_FINVALID; 372 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 373 i - 1].value = val; 374 } 375 } else { 376 /* UCH is a voltage input */ 377 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 378 VT1211_HM_UCH1 + i - 1); 379 val = reg0; 380 381 /* Convert to uV */ 382 /* XXX: conversion function is guessed */ 383 rfact = sc->sc_hm_sensors[VT1211_HMS_UCH1 + 384 i - 1].rfact; 385 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].value = 386 ((val * 100000000000ULL) / (rfact * 958)); 387 } 388 } 389 390 /* Read internal +3.3V */ 391 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_33V); 392 val = reg0; 393 394 /* Convert to uV */ 395 /* XXX: conversion function is guessed */ 396 rfact = sc->sc_hm_sensors[VT1211_HMS_33V].rfact; 397 sc->sc_hm_sensors[VT1211_HMS_33V].value = ((val * 100000000000ULL) / 398 (rfact * 958)); 399 400 /* Read FAN1 clock counter and divisor */ 401 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN1); 402 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); 403 reg1 = VT1211_HM_FSCTL_DIV1(reg1); 404 val = reg0 << reg1; 405 406 /* Convert to RPM */ 407 /* XXX: conversion function is guessed */ 408 if (val != 0) { 409 sc->sc_hm_sensors[VT1211_HMS_FAN1].value = 410 (sc->sc_hm_clock * 60 / 2) / val; 411 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags &= ~SENSOR_FINVALID; 412 } else { 413 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags |= SENSOR_FINVALID; 414 } 415 416 /* Read FAN2 clock counter and divisor */ 417 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN2); 418 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); 419 reg1 = VT1211_HM_FSCTL_DIV2(reg1); 420 val = reg0 << reg1; 421 422 /* Convert to RPM */ 423 /* XXX: conversion function is guessed */ 424 if (val != 0) { 425 sc->sc_hm_sensors[VT1211_HMS_FAN2].value = 426 (sc->sc_hm_clock * 60 / 2) / val; 427 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags &= ~SENSOR_FINVALID; 428 } else { 429 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags |= SENSOR_FINVALID; 430 } 431 432 timeout_add(&sc->sc_hm_timo, hz); 433 } 434