it.c revision 1.28
1/* $OpenBSD: it.c,v 1.28 2008/04/03 20:28:05 form Exp $ */ 2 3/* 4 * Copyright (c) 2007-2008 Oleg Safiullin <form@pdp-11.org.ru> 5 * Copyright (c) 2006-2007 Juan Romero Pardines <juan@xtrarom.org> 6 * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/device.h> 33#include <sys/sensors.h> 34 35#include <machine/bus.h> 36 37#include <dev/isa/isareg.h> 38#include <dev/isa/isavar.h> 39#include <dev/isa/itvar.h> 40 41 42#if defined(ITDEBUG) 43#define DPRINTF(x) do { printf x; } while (0) 44#else 45#define DPRINTF(x) 46#endif 47 48 49int it_match(struct device *, void *, void *); 50void it_attach(struct device *, struct device *, void *); 51u_int8_t it_readreg(bus_space_tag_t, bus_space_handle_t, int); 52void it_writereg(bus_space_tag_t, bus_space_handle_t, int, u_int8_t); 53void it_enter(bus_space_tag_t, bus_space_handle_t, int); 54void it_exit(bus_space_tag_t, bus_space_handle_t); 55 56u_int8_t it_ec_readreg(struct it_softc *, int); 57void it_ec_writereg(struct it_softc *, int, u_int8_t); 58void it_ec_refresh(void *arg); 59 60int it_wdog_cb(void *, int); 61 62 63struct { 64 int type; 65 const char *desc; 66} it_sensors[IT_EC_NUMSENSORS] = { 67#define IT_TEMP_BASE 0 68#define IT_TEMP_COUNT 3 69 { SENSOR_TEMP, NULL }, 70 { SENSOR_TEMP, NULL }, 71 { SENSOR_TEMP, NULL }, 72 73#define IT_FAN_BASE 3 74#define IT_FAN_COUNT 3 75 { SENSOR_FANRPM, NULL }, 76 { SENSOR_FANRPM, NULL }, 77 { SENSOR_FANRPM, NULL }, 78 79#define IT_VOLT_BASE 6 80#define IT_VOLT_COUNT 9 81 { SENSOR_VOLTS_DC, "VCORE_A" }, 82 { SENSOR_VOLTS_DC, "VCORE_B" }, 83 { SENSOR_VOLTS_DC, "+3.3V" }, 84 { SENSOR_VOLTS_DC, "+5V" }, 85 { SENSOR_VOLTS_DC, "+12V" }, 86 { SENSOR_VOLTS_DC, "-5V" }, 87 { SENSOR_VOLTS_DC, "-12V" }, 88 { SENSOR_VOLTS_DC, "+5VSB" }, 89 { SENSOR_VOLTS_DC, "VBAT" } 90}; 91 92#define RFACT_NONE 10000 93#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y)) 94 95int it_vrfact[IT_VOLT_COUNT] = { 96 RFACT_NONE, RFACT_NONE, RFACT_NONE, RFACT(68, 100), RFACT(30, 10), 97 RFACT(21, 10), RFACT(83, 20), 98 RFACT(68, 100), RFACT_NONE 99}; 100 101LIST_HEAD(, it_softc) it_softc_list = LIST_HEAD_INITIALIZER(&it_softc_list); 102 103 104int 105it_match(struct device *parent, void *match, void *aux) 106{ 107 struct isa_attach_args *ia = aux; 108 struct it_softc *sc; 109 bus_space_handle_t ioh; 110 int ec_iobase, found = 0; 111 u_int16_t cr; 112 113 if (ia->ipa_io[0].base != IO_IT1 && ia->ipa_io[0].base != IO_IT2) 114 return (0); 115 116 /* map i/o space */ 117 if (bus_space_map(ia->ia_iot, ia->ipa_io[0].base, 2, 0, &ioh) != 0) { 118 DPRINTF(("it_match: can't map i/o space")); 119 return (0); 120 } 121 122 /* enter MB PnP mode */ 123 it_enter(ia->ia_iot, ioh, ia->ipa_io[0].base); 124 125 /* 126 * SMSC or similar SuperIO chips use 0x55 magic to enter PnP mode 127 * and 0xaa to exit. These chips also enter PnP mode via ITE 128 * `enter MB PnP mode' sequence, so force chip to exit PnP mode 129 * if this is the case. 130 */ 131 bus_space_write_1(ia->ia_iot, ioh, IT_IO_ADDR, 0xaa); 132 133 /* get chip id */ 134 cr = it_readreg(ia->ia_iot, ioh, IT_CHIPID1) << 8; 135 cr |= it_readreg(ia->ia_iot, ioh, IT_CHIPID2); 136 137 switch (cr) { 138 case IT_ID_8705: 139 case IT_ID_8712: 140 case IT_ID_8716: 141 case IT_ID_8718: 142 case IT_ID_8726: 143 /* get environment controller base address */ 144 it_writereg(ia->ia_iot, ioh, IT_LDN, IT_EC_LDN); 145 ec_iobase = it_readreg(ia->ia_iot, ioh, IT_EC_MSB) << 8; 146 ec_iobase |= it_readreg(ia->ia_iot, ioh, IT_EC_LSB); 147 148 /* check if device already attached */ 149 LIST_FOREACH(sc, &it_softc_list, sc_list) 150 if (sc->sc_ec_iobase == ec_iobase) 151 break; 152 153 if (sc == NULL) { 154 ia->ipa_nio = 1; 155 ia->ipa_io[0].length = 2; 156 ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0; 157 found++; 158 } 159 160 break; 161 } 162 163 /* exit MB PnP mode */ 164 it_exit(ia->ia_iot, ioh); 165 166 /* unmap i/o space */ 167 bus_space_unmap(ia->ia_iot, ioh, 2); 168 169 return (found); 170} 171 172void 173it_attach(struct device *parent, struct device *self, void *aux) 174{ 175 struct it_softc *sc = (void *)self; 176 struct isa_attach_args *ia = aux; 177 int i; 178 u_int8_t cr; 179 180 sc->sc_iot = ia->ia_iot; 181 sc->sc_iobase = ia->ipa_io[0].base; 182 if (bus_space_map(sc->sc_iot, sc->sc_iobase, 2, 0, &sc->sc_ioh) != 0) { 183 printf(": can't map i/o space\n"); 184 return; 185 } 186 187 /* enter MB PnP mode */ 188 it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase); 189 190 /* get chip id and rev */ 191 sc->sc_chipid = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID1) << 8; 192 sc->sc_chipid |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID2); 193 sc->sc_chiprev = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPREV); 194 195 /* get environment controller base address */ 196 it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_EC_LDN); 197 sc->sc_ec_iobase = it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_MSB) << 8; 198 sc->sc_ec_iobase |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_LSB); 199 200 /* initialize watchdog */ 201 if (sc->sc_chipid != IT_ID_8705) { 202 it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN); 203 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_CSR, 0x00); 204 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x80); 205 wdog_register(sc, it_wdog_cb); 206 } 207 208 /* exit MB PnP mode and unmap */ 209 it_exit(sc->sc_iot, sc->sc_ioh); 210 211 LIST_INSERT_HEAD(&it_softc_list, sc, sc_list); 212 213 printf(": IT%xF rev 0x%02x", sc->sc_chipid, sc->sc_chiprev); 214 215 if (sc->sc_ec_iobase == 0) { 216 printf(", EC disabled\n"); 217 return; 218 } 219 220 printf(", EC port 0x%x\n", sc->sc_ec_iobase); 221 222 /* map environment controller i/o space */ 223 sc->sc_ec_iot = ia->ia_iot; 224 if (bus_space_map(sc->sc_ec_iot, sc->sc_ec_iobase, 8, 0, 225 &sc->sc_ec_ioh) != 0) { 226 printf("%s: can't map EC i/o space\n", sc->sc_dev.dv_xname); 227 return; 228 } 229 230 /* initialize sensor structures */ 231 for (i = 0; i < IT_EC_NUMSENSORS; i++) { 232 sc->sc_sensors[i].type = it_sensors[i].type; 233 234 if (it_sensors[i].desc != NULL) 235 snprintf(sc->sc_sensors[i].desc, 236 sizeof(sc->sc_sensors[i].desc), 237 it_sensors[i].desc); 238 } 239 240 /* register update task */ 241 if (sensor_task_register(sc, it_ec_refresh, IT_EC_INTERVAL) == NULL) { 242 printf(": unable to register update task\n", 243 sc->sc_dev.dv_xname); 244 bus_space_unmap(sc->sc_ec_iot, sc->sc_ec_ioh, 8); 245 return; 246 } 247 248 /* activate monitoring */ 249 cr = it_ec_readreg(sc, IT_EC_CFG); 250 it_ec_writereg(sc, IT_EC_CFG, cr | 0x09); 251 252 /* initialize sensors */ 253 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 254 sizeof(sc->sc_sensordev.xname)); 255 for (i = 0; i < IT_EC_NUMSENSORS; i++) 256 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); 257 sensordev_install(&sc->sc_sensordev); 258} 259 260u_int8_t 261it_readreg(bus_space_tag_t iot, bus_space_handle_t ioh, int r) 262{ 263 bus_space_write_1(iot, ioh, IT_IO_ADDR, r); 264 return (bus_space_read_1(iot, ioh, IT_IO_DATA)); 265} 266 267void 268it_writereg(bus_space_tag_t iot, bus_space_handle_t ioh, int r, u_int8_t v) 269{ 270 bus_space_write_1(iot, ioh, IT_IO_ADDR, r); 271 bus_space_write_1(iot, ioh, IT_IO_DATA, v); 272} 273 274void 275it_enter(bus_space_tag_t iot, bus_space_handle_t ioh, int iobase) 276{ 277 bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x87); 278 bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x01); 279 bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55); 280 if (iobase == IO_IT1) 281 bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55); 282 else 283 bus_space_write_1(iot, ioh, IT_IO_ADDR, 0xaa); 284} 285 286void 287it_exit(bus_space_tag_t iot, bus_space_handle_t ioh) 288{ 289 bus_space_write_1(iot, ioh, IT_IO_ADDR, IT_CCR); 290 bus_space_write_1(iot, ioh, IT_IO_DATA, 0x02); 291} 292 293u_int8_t 294it_ec_readreg(struct it_softc *sc, int r) 295{ 296 bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r); 297 return (bus_space_read_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA)); 298} 299 300void 301it_ec_writereg(struct it_softc *sc, int r, u_int8_t v) 302{ 303 bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r); 304 bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA, v); 305} 306 307void 308it_ec_refresh(void *arg) 309{ 310 struct it_softc *sc = arg; 311 int i, sdata, mode, divisor, odivisor, ndivisor; 312 313 /* refresh temp sensors */ 314 for (i = 0; i < IT_TEMP_COUNT; i++) { 315 sdata = it_ec_readreg(sc, IT_EC_TEMPBASE + i); 316 /* convert to degF */ 317 sc->sc_sensors[IT_TEMP_BASE + i].value = 318 sdata * 1000000 + 273150000; 319 } 320 321 /* refresh volt sensors */ 322 for (i = 0; i < IT_VOLT_COUNT; i++) { 323 sdata = it_ec_readreg(sc, IT_EC_VOLTBASE + i); 324 /* voltage returned as (mV >> 4) */ 325 sc->sc_sensors[IT_VOLT_BASE + i].value = sdata << 4; 326 /* these two values are negative and formula is different */ 327 if (i == 5 || i == 6) 328 sc->sc_sensors[IT_VOLT_BASE + i].value -= IT_EC_VREF; 329 /* rfact is (factor * 10^4) */ 330 sc->sc_sensors[IT_VOLT_BASE + i].value *= it_vrfact[i]; 331 /* division by 10 gets us back to uVDC */ 332 sc->sc_sensors[IT_VOLT_BASE + i].value /= 10; 333 if (i == 5 || i == 6) 334 sc->sc_sensors[IT_VOLT_BASE + i].value += 335 IT_EC_VREF * 1000; 336 } 337 338 /* refresh fan sensors */ 339 if (sc->sc_chipid == IT_ID_8705 || sc->sc_chipid == IT_ID_8712) 340 odivisor = ndivisor = divisor = 341 it_ec_readreg(sc, IT_EC_FAN_DIV); 342 else { 343 mode = it_ec_readreg(sc, IT_EC_FAN_ECR); 344 divisor = -1; 345 } 346 347 for (i = 0; i < IT_FAN_COUNT; i++) { 348 sc->sc_sensors[IT_FAN_BASE + i].flags &= ~SENSOR_FINVALID; 349 sdata = it_ec_readreg(sc, IT_EC_FANBASE + i); 350 351 if (divisor != -1) { 352 /* 353 * Use 8-bit FAN Tachometer & FAN Divisor registers 354 */ 355 if (sdata == 0xff) { 356 sc->sc_sensors[IT_FAN_BASE + i].flags |= 357 SENSOR_FINVALID; 358 if (i == 2) 359 ndivisor ^= 0x40; 360 else { 361 ndivisor &= ~(7 << (i * 3)); 362 ndivisor |= ((divisor + 1) & 7) << 363 (i * 3); 364 } 365 } else if (sdata != 0) { 366 if (i == 2) 367 divisor = divisor & 1 ? 3 : 1; 368 sc->sc_sensors[IT_FAN_BASE + i].value = 369 1350000 / (sdata << (divisor & 7)); 370 } else 371 sc->sc_sensors[IT_FAN_BASE + i].value = 0; 372 373 if (ndivisor != odivisor) 374 it_ec_writereg(sc, IT_EC_FAN_DIV, ndivisor); 375 } else { 376 /* 377 * Use 16-bit FAN tachometer register 378 */ 379 if (mode & (1 << i)) 380 sdata |= it_ec_readreg(sc, 381 IT_EC_FANEXTBASE + i) << 8; 382 if (sdata == ((mode & (1 << i)) ? 0xffff : 0xff)) 383 sc->sc_sensors[IT_FAN_BASE + i].flags |= 384 SENSOR_FINVALID; 385 else if (sdata != 0) 386 sc->sc_sensors[IT_FAN_BASE + i].value = 387 675000 / sdata; 388 else 389 sc->sc_sensors[IT_FAN_BASE + i].value = 0; 390 } 391 } 392} 393 394int 395it_wdog_cb(void *arg, int period) 396{ 397 struct it_softc *sc = arg; 398 399 /* enter MB PnP mode and select WDT device */ 400 it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase); 401 it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN); 402 403 /* disable watchdog timeout */ 404 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x80); 405 406 /* 1000s should be enough for everyone */ 407 if (period > 1000) 408 period = 1000; 409 else if (period < 0) 410 period = 0; 411 412 /* set watchdog timeout */ 413 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_MSB, period >> 8); 414 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_LSB, period & 0xff); 415 416 if (period > 0) 417 /* enable watchdog timeout */ 418 it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0xc0); 419 420 /* exit MB PnP mode */ 421 it_exit(sc->sc_iot, sc->sc_ioh); 422 423 return (period); 424} 425 426 427struct cfattach it_ca = { 428 sizeof(struct it_softc), 429 it_match, 430 it_attach 431}; 432 433struct cfdriver it_cd = { 434 NULL, "it", DV_DULL 435}; 436