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