1 /* $OpenBSD: fins.c,v 1.4 2014/12/10 12:27:57 mikeb Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 Mark Kettenis 5 * Copyright (c) 2007, 2008 Geoff Steckel 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/kernel.h> 24 #include <sys/queue.h> 25 #include <sys/sensors.h> 26 #include <machine/bus.h> 27 28 #include <dev/isa/isareg.h> 29 #include <dev/isa/isavar.h> 30 31 /* Derived from LM78 code. Only handles chips attached to ISA bus */ 32 33 /* 34 * Fintek F71805 registers and constants 35 * http://www.fintek.com.tw/files/productfiles/F71805F_V025.pdf 36 * This chip is a multi-io chip with many functions. 37 * Each function may be relocated in I/O space by the BIOS. 38 * The base address (2E or 4E) accesses a configuration space which 39 * has pointers to the individual functions. The config space must be 40 * unlocked with a cookie and relocked afterwards. The chip ID is stored 41 * in config space so it is not normally visible. 42 * 43 * We assume that the monitor is enabled. We don't try to start or stop it. 44 * The voltage dividers specified are from reading the chips on one board. 45 * There is no way to determine what they are in the general case. 46 * This section of the chip controls the fans. We don't do anything to them. 47 */ 48 49 #define FINS_UNLOCK 0x87 /* magic constant - write 2x to select chip */ 50 #define FINS_LOCK 0xaa /* magic constant - write 1x to deselect reg */ 51 52 /* ISA registers index to an internal register space on chip */ 53 #define FINS_ADDR 0x00 54 #define FINS_DATA 0x01 55 56 #define FINS_FUNC_SEL 0x07 /* select which chip function to access */ 57 #define FINS_CHIP 0x20 /* chip ID */ 58 #define FINS_MANUF 0x23 /* manufacturer ID */ 59 #define FINS_BASEADDR 0x60 /* I/O base of chip function */ 60 61 #define FINS_71806 0x0341 /* same as F71872 */ 62 #define FINS_71805 0x0406 63 #define FINS_71882 0x0541 /* same as F71883 */ 64 #define FINS_71862 0x0601 /* same as F71863 */ 65 #define FINTEK_ID 0x1934 66 67 #define FINS_FUNC_SENSORS 0x04 68 #define FINS_FUNC_WATCHDOG 0x07 69 70 /* sensors device registers */ 71 #define FINS_SENS_TMODE(sc) ((sc)->fins_chipid <= FINS_71805 ? 0x01 : 0x6b) 72 #define FINS_SENS_VDIVS 0x0e 73 74 /* watchdog device registers (mapped straight to i/o port offsets) */ 75 #define FINS_WDOG_CR0 0x00 76 #define FINS_WDOG_CR1 0x05 77 #define FINS_WDOG_TIMER 0x06 78 79 /* CR0 flags */ 80 #define FINS_WDOG_OUTEN 0x80 81 82 /* CR1 flags */ 83 #define FINS_WDOG_EN 0x20 84 #define FINS_WDOG_MINS 0x08 85 86 #define FINS_MAX_SENSORS 18 87 /* 88 * Fintek chips typically measure voltages using 8mv steps. 89 * To measure higher voltages the input is attenuated with (external) 90 * resistors. Negative voltages are measured using inverting op amps 91 * and resistors. So we have to convert the sensor values back to 92 * real voltages by applying the appropriate resistor factor. 93 */ 94 #define FRFACT_NONE 8000 95 #define FRFACT(x, y) (FRFACT_NONE * ((x) + (y)) / (y)) 96 #define FNRFACT(x, y) (-FRFACT_NONE * (x) / (y)) 97 98 struct fins_softc; 99 100 struct fins_sensor { 101 char *fs_desc; 102 void (*fs_refresh)(struct fins_softc *, int); 103 enum sensor_type fs_type; 104 int fs_aux; 105 u_int8_t fs_reg; 106 }; 107 108 struct fins_softc { 109 struct device sc_dev; 110 111 struct ksensor fins_ksensors[FINS_MAX_SENSORS]; 112 struct ksensordev fins_sensordev; 113 struct sensor_task *fins_sensortask; 114 struct fins_sensor *fins_sensors; 115 116 bus_space_handle_t sc_ioh_sens; 117 bus_space_handle_t sc_ioh_wdog; 118 bus_space_tag_t sc_iot; 119 120 u_int16_t fins_chipid; 121 u_int8_t fins_tempsel; 122 u_int8_t fins_wdog_cr; 123 }; 124 125 int fins_match(struct device *, void *, void *); 126 void fins_attach(struct device *, struct device *, void *); 127 int fins_activate(struct device *, int); 128 129 void fins_unlock(bus_space_tag_t, bus_space_handle_t); 130 void fins_lock(bus_space_tag_t, bus_space_handle_t); 131 132 u_int8_t fins_read(bus_space_tag_t, bus_space_handle_t, int); 133 u_int16_t fins_read_2(bus_space_tag_t, bus_space_handle_t, int); 134 void fins_write(bus_space_tag_t, bus_space_handle_t, int, u_int8_t); 135 136 static __inline u_int8_t fins_read_sens(struct fins_softc *, int); 137 static __inline u_int16_t fins_read_sens_2(struct fins_softc *, int); 138 139 static __inline u_int8_t fins_read_wdog(struct fins_softc *, int); 140 static __inline void fins_write_wdog(struct fins_softc *, int, u_int8_t); 141 142 void fins_setup_sensors(struct fins_softc *, struct fins_sensor *); 143 void fins_refresh(void *); 144 145 void fins_get_rpm(struct fins_softc *, int); 146 void fins_get_temp(struct fins_softc *, int); 147 void fins_get_volt(struct fins_softc *, int); 148 149 int fins_wdog_cb(void *, int); 150 151 struct cfattach fins_ca = { 152 sizeof(struct fins_softc), 153 fins_match, 154 fins_attach, 155 NULL, 156 fins_activate 157 }; 158 159 struct cfdriver fins_cd = { 160 NULL, "fins", DV_DULL 161 }; 162 163 struct fins_sensor fins_71805_sensors[] = { 164 { "+3.3V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x10 }, 165 { "Vtt", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x11 }, 166 { "Vram", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x12 }, 167 { "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100), 0x13 }, 168 { "+5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x14 }, 169 { "+12V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20), 0x15 }, 170 { "+1.5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x16 }, 171 { "Vcore", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x17 }, 172 { "Vsb", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x18 }, 173 { "Vsbint", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x19 }, 174 { "Vbat", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x1a }, 175 176 { NULL, fins_get_temp, SENSOR_TEMP, 0x01, 0x1b }, 177 { NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x1c }, 178 { NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x1d }, 179 180 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x20 }, 181 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x22 }, 182 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x24 }, 183 184 { NULL } 185 }; 186 187 struct fins_sensor fins_71882_sensors[] = { 188 { "+3.3V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x20 }, 189 { "Vcore", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x21 }, 190 { "Vram", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x22 }, 191 { "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100), 0x23 }, 192 { "+5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x24 }, 193 { "+12V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20), 0x25 }, 194 { "+1.5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x26 }, 195 { "Vsb", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x27 }, 196 { "Vbat", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x28 }, 197 198 { NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x72 }, 199 { NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x74 }, 200 { NULL, fins_get_temp, SENSOR_TEMP, 0x08, 0x76 }, 201 202 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xa0 }, 203 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xb0 }, 204 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xc0 }, 205 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xd0 }, 206 207 { NULL } 208 }; 209 210 int 211 fins_match(struct device *parent, void *match, void *aux) 212 { 213 struct isa_attach_args *ia = aux; 214 bus_space_handle_t ioh; 215 bus_space_tag_t iot; 216 int ret = 0; 217 u_int16_t id; 218 219 iot = ia->ia_iot; 220 if (bus_space_map(iot, ia->ipa_io[0].base, 2, 0, &ioh)) 221 return (0); 222 223 /* Fintek uses magic cookie locks to distinguish their chips */ 224 fins_unlock(iot, ioh); 225 226 fins_write(iot, ioh, FINS_FUNC_SEL, 0); /* IDs appear only in space 0 */ 227 if (fins_read_2(iot, ioh, FINS_MANUF) != FINTEK_ID) 228 goto match_done; 229 id = fins_read_2(iot, ioh, FINS_CHIP); 230 switch(id) { 231 case FINS_71882: 232 case FINS_71862: 233 ia->ipa_nio = 3; 234 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_WATCHDOG); 235 ia->ipa_io[2].base = fins_read_2(iot, ioh, FINS_BASEADDR); 236 ia->ipa_io[2].length = 8; 237 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS); 238 ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR); 239 ia->ipa_io[1].base += 5; 240 break; 241 case FINS_71806: 242 case FINS_71805: 243 ia->ipa_nio = 2; 244 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS); 245 ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR); 246 break; 247 default: 248 goto match_done; 249 } 250 ia->ipa_io[0].length = ia->ipa_io[1].length = 2; 251 ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0; 252 ia->ia_aux = (void *)(u_long)id; 253 ret = 1; 254 match_done: 255 fins_lock(iot, ioh); 256 return (ret); 257 } 258 259 void 260 fins_attach(struct device *parent, struct device *self, void *aux) 261 { 262 struct fins_softc *sc = (struct fins_softc *)self; 263 struct isa_attach_args *ia = aux; 264 bus_addr_t iobase; 265 u_int32_t iosize; 266 u_int i; 267 268 sc->sc_iot = ia->ia_iot; 269 sc->fins_chipid = (u_int16_t)(u_long)ia->ia_aux; 270 iobase = ia->ipa_io[1].base; 271 iosize = ia->ipa_io[1].length; 272 if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_sens)) { 273 printf(": can't map sensor i/o space\n"); 274 return; 275 } 276 switch(sc->fins_chipid) { 277 case FINS_71882: 278 case FINS_71862: 279 fins_setup_sensors(sc, fins_71882_sensors); 280 break; 281 case FINS_71806: 282 case FINS_71805: 283 fins_setup_sensors(sc, fins_71805_sensors); 284 break; 285 } 286 sc->fins_sensortask = sensor_task_register(sc, fins_refresh, 5); 287 if (sc->fins_sensortask == NULL) { 288 printf(": can't register update task\n"); 289 return; 290 } 291 for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i) 292 sensor_attach(&sc->fins_sensordev, &sc->fins_ksensors[i]); 293 sensordev_install(&sc->fins_sensordev); 294 295 if (sc->fins_chipid <= FINS_71805) 296 goto attach_done; 297 iobase = ia->ipa_io[2].base; 298 iosize = ia->ipa_io[2].length; 299 if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_wdog)) { 300 printf(": can't map watchdog i/o space\n"); 301 return; 302 } 303 sc->fins_wdog_cr = fins_read_wdog(sc, FINS_WDOG_CR1); 304 sc->fins_wdog_cr &= ~(FINS_WDOG_MINS | FINS_WDOG_EN); 305 fins_write_wdog(sc, FINS_WDOG_CR1, sc->fins_wdog_cr); 306 wdog_register(fins_wdog_cb, sc); 307 attach_done: 308 printf("\n"); 309 } 310 311 int 312 fins_activate(struct device *self, int act) 313 { 314 switch (act) { 315 case DVACT_POWERDOWN: 316 wdog_shutdown(self); 317 break; 318 } 319 320 return (0); 321 } 322 323 u_int8_t 324 fins_read(bus_space_tag_t iot, bus_space_handle_t ioh, int reg) 325 { 326 bus_space_write_1(iot, ioh, FINS_ADDR, reg); 327 return (bus_space_read_1(iot, ioh, FINS_DATA)); 328 } 329 330 u_int16_t 331 fins_read_2(bus_space_tag_t iot, bus_space_handle_t ioh, int reg) 332 { 333 u_int16_t val; 334 335 bus_space_write_1(iot, ioh, FINS_ADDR, reg); 336 val = bus_space_read_1(iot, ioh, FINS_DATA) << 8; 337 bus_space_write_1(iot, ioh, FINS_ADDR, reg + 1); 338 return (val | bus_space_read_1(iot, ioh, FINS_DATA)); 339 } 340 341 void 342 fins_write(bus_space_tag_t iot, bus_space_handle_t ioh, int reg, u_int8_t val) 343 { 344 bus_space_write_1(iot, ioh, FINS_ADDR, reg); 345 bus_space_write_1(iot, ioh, FINS_DATA, val); 346 } 347 348 static __inline u_int8_t 349 fins_read_sens(struct fins_softc *sc, int reg) 350 { 351 return (fins_read(sc->sc_iot, sc->sc_ioh_sens, reg)); 352 } 353 354 static __inline u_int16_t 355 fins_read_sens_2(struct fins_softc *sc, int reg) 356 { 357 return (fins_read_2(sc->sc_iot, sc->sc_ioh_sens, reg)); 358 } 359 360 static __inline u_int8_t 361 fins_read_wdog(struct fins_softc *sc, int reg) 362 { 363 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh_wdog, reg)); 364 } 365 366 static __inline void 367 fins_write_wdog(struct fins_softc *sc, int reg, u_int8_t val) 368 { 369 bus_space_write_1(sc->sc_iot, sc->sc_ioh_wdog, reg, val); 370 } 371 372 void 373 fins_unlock(bus_space_tag_t iot, bus_space_handle_t ioh) 374 { 375 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK); 376 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK); 377 } 378 379 void 380 fins_lock(bus_space_tag_t iot, bus_space_handle_t ioh) 381 { 382 bus_space_write_1(iot, ioh, 0, FINS_LOCK); 383 bus_space_unmap(iot, ioh, 2); 384 } 385 386 void 387 fins_setup_sensors(struct fins_softc *sc, struct fins_sensor *sensors) 388 { 389 int i; 390 391 for (i = 0; sensors[i].fs_refresh != NULL; ++i) { 392 sc->fins_ksensors[i].type = sensors[i].fs_type; 393 if (sensors[i].fs_desc != NULL) 394 strlcpy(sc->fins_ksensors[i].desc, sensors[i].fs_desc, 395 sizeof(sc->fins_ksensors[i].desc)); 396 } 397 strlcpy(sc->fins_sensordev.xname, sc->sc_dev.dv_xname, 398 sizeof(sc->fins_sensordev.xname)); 399 sc->fins_sensors = sensors; 400 sc->fins_tempsel = fins_read_sens(sc, FINS_SENS_TMODE(sc)); 401 } 402 403 #if 0 404 void 405 fins_get_dividers(struct fins_softc *sc) 406 { 407 int i, p, m; 408 u_int16_t r = fins_read_sens_2(sc, FINS_SENS_VDIVS); 409 410 for (i = 0; i < 6; ++i) { 411 p = (i < 4) ? i : i + 2; 412 m = (r & (0x03 << p)) >> p; 413 if (m == 3) 414 m = 4; 415 fins_71882_sensors[i + 1].fs_aux = FRFACT_NONE << m; 416 } 417 } 418 #endif 419 420 void 421 fins_refresh(void *arg) 422 { 423 struct fins_softc *sc = arg; 424 int i; 425 426 for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i) 427 sc->fins_sensors[i].fs_refresh(sc, i); 428 } 429 430 void 431 fins_get_volt(struct fins_softc *sc, int n) 432 { 433 struct ksensor *sensor = &sc->fins_ksensors[n]; 434 struct fins_sensor *fs = &sc->fins_sensors[n]; 435 int data; 436 437 data = fins_read_sens(sc, fs->fs_reg); 438 if (data == 0xff || data == 0) { 439 sensor->flags |= SENSOR_FINVALID; 440 sensor->value = 0; 441 } else { 442 sensor->flags &= ~SENSOR_FINVALID; 443 sensor->value = data * fs->fs_aux; 444 } 445 } 446 447 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */ 448 void 449 fins_get_temp(struct fins_softc *sc, int n) 450 { 451 struct ksensor *sensor = &sc->fins_ksensors[n]; 452 struct fins_sensor *fs = &sc->fins_sensors[n]; 453 u_int data; 454 u_int max; 455 456 /* 457 * The data sheet says that the range of the temperature 458 * sensor is between 0 and 127 or 140 degrees C depending on 459 * what kind of sensor is used. 460 * A disconnected sensor seems to read over 110 or so. 461 */ 462 data = fins_read_sens(sc, fs->fs_reg); 463 max = (sc->fins_tempsel & fs->fs_aux) ? 111 : 128; 464 if (data == 0 || data >= max) { /* disconnected? */ 465 sensor->flags |= SENSOR_FINVALID; 466 sensor->value = 0; 467 } else { 468 sensor->flags &= ~SENSOR_FINVALID; 469 sensor->value = data * 1000000 + 273150000; 470 } 471 } 472 473 /* The chip holds a fudge factor for BJT sensors */ 474 /* this is currently unused but might be reenabled */ 475 #if 0 476 void 477 fins_refresh_offset(struct fins_softc *sc, int n) 478 { 479 struct ksensor *sensor = &sc->fins_ksensors[n]; 480 struct fins_sensor *fs = &sc->fins_sensors[n]; 481 u_int data; 482 483 sensor->flags &= ~SENSOR_FINVALID; 484 data = fins_read_sens(sc, fs->fs_reg); 485 data |= ~0 * (data & 0x40); /* sign extend 7-bit value */ 486 sensor->value = data * 1000000 + 273150000; 487 } 488 #endif 489 490 /* fan speed appears to be a 12-bit number */ 491 void 492 fins_get_rpm(struct fins_softc *sc, int n) 493 { 494 struct ksensor *sensor = &sc->fins_ksensors[n]; 495 struct fins_sensor *fs = &sc->fins_sensors[n]; 496 int data; 497 498 data = fins_read_sens_2(sc, fs->fs_reg); 499 if (data >= 0xfff) { 500 sensor->value = 0; 501 sensor->flags |= SENSOR_FINVALID; 502 } else { 503 sensor->value = 1500000 / data; 504 sensor->flags &= ~SENSOR_FINVALID; 505 } 506 } 507 508 int 509 fins_wdog_cb(void *arg, int period) 510 { 511 struct fins_softc *sc = arg; 512 u_int8_t cr0, cr1, t; 513 514 cr0 = fins_read_wdog(sc, FINS_WDOG_CR0) & ~FINS_WDOG_OUTEN; 515 fins_write_wdog(sc, FINS_WDOG_CR0, cr0); 516 517 cr1 = sc->fins_wdog_cr; 518 if (period > 0xff) { 519 cr1 |= FINS_WDOG_MINS; 520 t = (period + 59) / 60; 521 period = (int)t * 60; 522 } else if (period > 0) 523 t = period; 524 else 525 return (0); 526 527 fins_write_wdog(sc, FINS_WDOG_TIMER, t); 528 fins_write_wdog(sc, FINS_WDOG_CR0, cr0 | FINS_WDOG_OUTEN); 529 fins_write_wdog(sc, FINS_WDOG_CR1, cr1 | FINS_WDOG_EN); 530 return (period); 531 } 532