1 /* $OpenBSD: viasio.c,v 1.12 2009/03/29 21:53:52 sthen 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 #define VIASIO_CFFLAGS_WDG_ENABLE 0x0002 /* enable WDG if disabled */ 45 46 struct viasio_softc { 47 struct device sc_dev; 48 49 bus_space_tag_t sc_iot; 50 bus_space_handle_t sc_ioh; 51 52 /* Hardware monitor */ 53 bus_space_handle_t sc_hm_ioh; 54 int sc_hm_clock; 55 struct ksensor sc_hm_sensors[VT1211_HM_NSENSORS]; 56 struct ksensordev sc_sensordev; 57 struct timeout sc_hm_timo; 58 59 /* Watchdog timer */ 60 bus_space_handle_t sc_wdg_ioh; 61 }; 62 63 int viasio_probe(struct device *, void *, void *); 64 void viasio_attach(struct device *, struct device *, void *); 65 66 void viasio_hm_init(struct viasio_softc *); 67 void viasio_hm_refresh(void *); 68 69 void viasio_wdg_init(struct viasio_softc *); 70 int viasio_wdg_cb(void *, int); 71 72 struct cfattach viasio_ca = { 73 sizeof(struct viasio_softc), 74 viasio_probe, 75 viasio_attach 76 }; 77 78 struct cfdriver viasio_cd = { 79 NULL, "viasio", DV_DULL 80 }; 81 82 static __inline void 83 viasio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 84 { 85 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); 86 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC); 87 } 88 89 static __inline void 90 viasio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 91 { 92 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_DS_MAGIC); 93 } 94 95 static __inline u_int8_t 96 viasio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 97 { 98 bus_space_write_1(iot, ioh, VT1211_INDEX, index); 99 return (bus_space_read_1(iot, ioh, VT1211_DATA)); 100 } 101 102 static __inline void 103 viasio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 104 u_int8_t data) 105 { 106 bus_space_write_1(iot, ioh, VT1211_INDEX, index); 107 bus_space_write_1(iot, ioh, VT1211_DATA, data); 108 } 109 110 static __inline int64_t 111 viasio_raw2temp(int raw) 112 { 113 int tblsize = sizeof(vt1211_hm_temptbl) / sizeof(vt1211_hm_temptbl[0]); 114 int i; 115 int raw1, raw2; 116 int64_t temp = -1, temp1, temp2; 117 118 if (raw < vt1211_hm_temptbl[0].raw || 119 raw > vt1211_hm_temptbl[tblsize - 1].raw) 120 return (-1); 121 122 for (i = 0; i < tblsize - 1; i++) { 123 raw1 = vt1211_hm_temptbl[i].raw; 124 temp1 = vt1211_hm_temptbl[i].temp; 125 raw2 = vt1211_hm_temptbl[i + 1].raw; 126 temp2 = vt1211_hm_temptbl[i + 1].temp; 127 128 if (raw >= raw1 && raw <= raw2) { 129 /* linear interpolation */ 130 temp = temp1 + ((raw - raw1) * (temp2 - temp1)) / 131 (raw2 - raw1); 132 break; 133 } 134 } 135 136 return (temp); 137 } 138 139 int 140 viasio_probe(struct device *parent, void *match, void *aux) 141 { 142 struct isa_attach_args *ia = aux; 143 bus_space_tag_t iot; 144 bus_space_handle_t ioh; 145 u_int8_t reg; 146 147 /* Match by device ID */ 148 iot = ia->ia_iot; 149 if (bus_space_map(iot, ia->ipa_io[0].base, VT1211_IOSIZE, 0, &ioh)) 150 return (0); 151 viasio_conf_enable(iot, ioh); 152 reg = viasio_conf_read(iot, ioh, VT1211_ID); 153 DPRINTF(("viasio_probe: id 0x%02x\n", reg)); 154 viasio_conf_disable(iot, ioh); 155 bus_space_unmap(iot, ioh, VT1211_IOSIZE); 156 if (reg == VT1211_ID_VT1211) { 157 ia->ipa_nio = 1; 158 ia->ipa_io[0].length = VT1211_IOSIZE; 159 ia->ipa_nmem = 0; 160 ia->ipa_nirq = 0; 161 ia->ipa_ndrq = 0; 162 return (1); 163 } 164 165 return (0); 166 } 167 168 void 169 viasio_attach(struct device *parent, struct device *self, void *aux) 170 { 171 struct viasio_softc *sc = (void *)self; 172 struct isa_attach_args *ia = aux; 173 u_int8_t reg; 174 175 /* Map ISA I/O space */ 176 sc->sc_iot = ia->ia_iot; 177 if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, 178 VT1211_IOSIZE, 0, &sc->sc_ioh)) { 179 printf(": can't map i/o space\n"); 180 return; 181 } 182 183 /* Enter configuration mode */ 184 viasio_conf_enable(sc->sc_iot, sc->sc_ioh); 185 186 /* Read device revision */ 187 reg = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_REV); 188 printf(": VT1211 rev 0x%02x", reg); 189 190 /* Initialize logical devices */ 191 viasio_hm_init(sc); 192 viasio_wdg_init(sc); 193 printf("\n"); 194 195 /* Escape from configuration mode */ 196 viasio_conf_disable(sc->sc_iot, sc->sc_ioh); 197 } 198 199 void 200 viasio_hm_init(struct viasio_softc *sc) 201 { 202 u_int8_t reg0, reg1; 203 u_int16_t iobase; 204 int i; 205 206 printf(", HM"); 207 208 /* Select HM logical device */ 209 viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_HM); 210 211 /* 212 * Check if logical device is activated by firmware. If not 213 * try to activate it only if requested. 214 */ 215 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ACT); 216 DPRINTF((": ACT 0x%02x", reg0)); 217 if ((reg0 & VT1211_HM_ACT_EN) == 0) { 218 if ((sc->sc_dev.dv_cfdata->cf_flags & 219 VIASIO_CFFLAGS_HM_ENABLE) != 0) { 220 reg0 |= VT1211_HM_ACT_EN; 221 viasio_conf_write(sc->sc_iot, sc->sc_ioh, 222 VT1211_HM_ACT, reg0); 223 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, 224 VT1211_HM_ACT); 225 DPRINTF((", new ACT 0x%02x", reg0)); 226 if ((reg0 & VT1211_HM_ACT_EN) == 0) { 227 printf(" failed to activate"); 228 return; 229 } 230 } else { 231 printf(" not activated"); 232 return; 233 } 234 } 235 236 /* Read HM I/O space address */ 237 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_LSB); 238 reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_MSB); 239 iobase = (reg1 << 8) | reg0; 240 DPRINTF((", addr 0x%04x", iobase)); 241 242 /* Map HM I/O space */ 243 if (bus_space_map(sc->sc_iot, iobase, VT1211_HM_IOSIZE, 0, 244 &sc->sc_hm_ioh)) { 245 printf(" can't map i/o space"); 246 return; 247 } 248 249 /* 250 * Check if hardware monitoring is enabled by firmware. If not 251 * try to enable it only if requested. 252 */ 253 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_CONF); 254 DPRINTF((", CONF 0x%02x", reg0)); 255 if ((reg0 & VT1211_HM_CONF_START) == 0) { 256 if ((sc->sc_dev.dv_cfdata->cf_flags & 257 VIASIO_CFFLAGS_HM_ENABLE) != 0) { 258 reg0 |= VT1211_HM_CONF_START; 259 bus_space_write_1(sc->sc_iot, sc->sc_hm_ioh, 260 VT1211_HM_CONF, reg0); 261 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 262 VT1211_HM_CONF); 263 DPRINTF((", new CONF 0x%02x", reg0)); 264 if ((reg0 & VT1211_HM_CONF_START) == 0) { 265 printf(" failed to enable monitoring"); 266 return; 267 } 268 } else { 269 printf(" monitoring not enabled"); 270 return; 271 } 272 } 273 274 /* Read PWM clock frequency */ 275 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_PWMCS); 276 sc->sc_hm_clock = vt1211_hm_clock[reg0 & 0x07]; 277 DPRINTF((", PWMCS 0x%02x, %dHz", reg0, sc->sc_hm_clock)); 278 279 /* Temperature reading 1 */ 280 sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP; 281 282 /* Universal channels (UCH) 1-5 */ 283 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_UCHCONF); 284 DPRINTF((", UCHCONF 0x%02x", reg0)); 285 for (i = 1; i <= 5; i++) { 286 /* UCH can be configured either as thermal or voltage input */ 287 if (VT1211_HM_UCHCONF_ISTEMP(reg0, i)) { 288 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = 289 SENSOR_TEMP; 290 } else { 291 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type = 292 SENSOR_VOLTS_DC; 293 } 294 snprintf(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc, 295 sizeof(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc), 296 "UCH%d", i); 297 } 298 299 /* Internal +3.3V */ 300 sc->sc_hm_sensors[VT1211_HMS_33V].type = SENSOR_VOLTS_DC; 301 strlcpy(sc->sc_hm_sensors[VT1211_HMS_33V].desc, "+3.3V", 302 sizeof(sc->sc_hm_sensors[VT1211_HMS_33V].desc)); 303 304 /* FAN reading 1, 2 */ 305 sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM; 306 sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM; 307 308 /* Start sensors */ 309 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 310 sizeof(sc->sc_sensordev.xname)); 311 for (i = 0; i < VT1211_HM_NSENSORS; i++) 312 sensor_attach(&sc->sc_sensordev, &sc->sc_hm_sensors[i]); 313 sensordev_install(&sc->sc_sensordev); 314 timeout_set(&sc->sc_hm_timo, viasio_hm_refresh, sc); 315 timeout_add_sec(&sc->sc_hm_timo, 1); 316 } 317 318 void 319 viasio_hm_refresh(void *arg) 320 { 321 struct viasio_softc *sc = arg; 322 u_int8_t reg0, reg1; 323 int64_t val, rfact; 324 int i; 325 326 /* TEMP1 is a 10-bit thermal input */ 327 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TEMP1); 328 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TCONF1); 329 reg1 = VT1211_HM_TCONF1_TEMP1(reg1); 330 val = (reg0 << 2) | reg1; 331 332 /* Convert to uK */ 333 /* XXX: conversion function is guessed */ 334 val = viasio_raw2temp(val); 335 if (val == -1) { 336 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags |= SENSOR_FINVALID; 337 } else { 338 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags &= ~SENSOR_FINVALID; 339 sc->sc_hm_sensors[VT1211_HMS_TEMP1].value = val; 340 } 341 342 /* Universal channels 1-5 */ 343 for (i = 1; i <= 5; i++) { 344 if (sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type == 345 SENSOR_TEMP) { 346 /* UCH is a 10-bit thermal input */ 347 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 348 VT1211_HM_UCH1 + i - 1); 349 if (i == 1) { 350 reg1 = bus_space_read_1(sc->sc_iot, 351 sc->sc_hm_ioh, VT1211_HM_VID4); 352 reg1 = VT1211_HM_VID4_UCH1(reg1); 353 } else { 354 reg1 = bus_space_read_1(sc->sc_iot, 355 sc->sc_hm_ioh, VT1211_HM_ETR); 356 reg1 = VT1211_HM_ETR_UCH(reg1, i); 357 } 358 val = (reg0 << 2) | reg1; 359 360 /* Convert to uK */ 361 /* XXX: conversion function is guessed */ 362 val = viasio_raw2temp(val); 363 if (val == -1) { 364 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 365 i - 1].flags |= SENSOR_FINVALID; 366 } else { 367 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 368 i - 1].flags &= ~SENSOR_FINVALID; 369 sc->sc_hm_sensors[VT1211_HMS_UCH1 + 370 i - 1].value = val; 371 } 372 } else { 373 /* UCH is a voltage input */ 374 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, 375 VT1211_HM_UCH1 + i - 1); 376 val = reg0; 377 378 /* Convert to uV */ 379 /* XXX: conversion function is guessed */ 380 rfact = vt1211_hm_vrfact[i - 1]; 381 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].value = 382 ((val * 100000000000ULL) / (rfact * 958)); 383 } 384 } 385 386 /* Read internal +3.3V */ 387 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_33V); 388 val = reg0; 389 390 /* Convert to uV */ 391 /* XXX: conversion function is guessed */ 392 rfact = vt1211_hm_vrfact[5]; 393 sc->sc_hm_sensors[VT1211_HMS_33V].value = ((val * 100000000000ULL) / 394 (rfact * 958)); 395 396 /* Read FAN1 clock counter and divisor */ 397 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN1); 398 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); 399 reg1 = VT1211_HM_FSCTL_DIV1(reg1); 400 val = reg0 << reg1; 401 402 /* Convert to RPM */ 403 /* XXX: conversion function is guessed */ 404 if (val != 0) { 405 sc->sc_hm_sensors[VT1211_HMS_FAN1].value = 406 (sc->sc_hm_clock * 60 / 2) / val; 407 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags &= ~SENSOR_FINVALID; 408 } else { 409 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags |= SENSOR_FINVALID; 410 } 411 412 /* Read FAN2 clock counter and divisor */ 413 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN2); 414 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL); 415 reg1 = VT1211_HM_FSCTL_DIV2(reg1); 416 val = reg0 << reg1; 417 418 /* Convert to RPM */ 419 /* XXX: conversion function is guessed */ 420 if (val != 0) { 421 sc->sc_hm_sensors[VT1211_HMS_FAN2].value = 422 (sc->sc_hm_clock * 60 / 2) / val; 423 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags &= ~SENSOR_FINVALID; 424 } else { 425 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags |= SENSOR_FINVALID; 426 } 427 428 timeout_add_sec(&sc->sc_hm_timo, 1); 429 } 430 431 void 432 viasio_wdg_init(struct viasio_softc *sc) 433 { 434 u_int8_t reg0, reg1; 435 u_int16_t iobase; 436 437 printf(", WDG"); 438 439 /* Select WDG logical device */ 440 viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_WDG); 441 442 /* 443 * Check if logical device is activated by firmware. If not 444 * try to activate it only if requested. 445 */ 446 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ACT); 447 DPRINTF((": ACT 0x%02x", reg0)); 448 if ((reg0 & VT1211_WDG_ACT_EN) == 0) { 449 if ((sc->sc_dev.dv_cfdata->cf_flags & 450 VIASIO_CFFLAGS_WDG_ENABLE) != 0) { 451 reg0 |= VT1211_WDG_ACT_EN; 452 viasio_conf_write(sc->sc_iot, sc->sc_ioh, 453 VT1211_WDG_ACT, reg0); 454 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, 455 VT1211_WDG_ACT); 456 DPRINTF((", new ACT 0x%02x", reg0)); 457 if ((reg0 & VT1211_WDG_ACT_EN) == 0) { 458 printf(" failed to activate"); 459 return; 460 } 461 } else { 462 printf(" not activated"); 463 return; 464 } 465 } 466 467 /* Read WDG I/O space address */ 468 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_LSB); 469 reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_MSB); 470 iobase = (reg1 << 8) | reg0; 471 DPRINTF((", addr 0x%04x", iobase)); 472 473 /* Map WDG I/O space */ 474 if (bus_space_map(sc->sc_iot, iobase, VT1211_WDG_IOSIZE, 0, 475 &sc->sc_wdg_ioh)) { 476 printf(" can't map i/o space"); 477 return; 478 } 479 480 /* Register new watchdog */ 481 wdog_register(sc, viasio_wdg_cb); 482 } 483 484 int 485 viasio_wdg_cb(void *arg, int period) 486 { 487 struct viasio_softc *sc = arg; 488 int mins; 489 490 mins = (period + 59) / 60; 491 if (mins > 255) 492 mins = 255; 493 494 bus_space_write_1(sc->sc_iot, sc->sc_wdg_ioh, VT1211_WDG_TIMEOUT, mins); 495 DPRINTF(("viasio_wdg_cb: %d mins\n", mins)); 496 497 return (mins * 60); 498 } 499