Annotation of sys/dev/isa/viasio.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: viasio.c,v 1.10 2007/06/14 19:13:37 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 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(&sc->sc_hm_timo, hz);
! 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(&sc->sc_hm_timo, hz);
! 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: }
CVSweb