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