1 /* $OpenBSD: fins.c,v 1.1 2008/03/19 19:33:09 deraadt 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 50 #define FINS_UNLOCK 0x87 /* magic constant - write 2x to select chip */ 51 #define FINS_LOCK 0xaa /* magic constant - write 1x to deselect reg */ 52 53 #define FINS_FUNC_SEL 0x07 /* select which subchip to access */ 54 # define FINS_FUNC_SENSORS 0x4 55 56 /* ISA registers index to an internal register space on chip */ 57 #define FINS_ADDR 0x00 58 #define FINS_DATA 0x01 59 60 /* Chip identification regs and values in bank 0 */ 61 #define FINS_MANUF 0x23 /* manufacturer ID */ 62 # define FINTEK_ID 0x1934 63 #define FINS_CHIP 0x20 /* chip ID */ 64 # define FINS_ID 0x0406 65 66 /* in bank sensors of config space */ 67 #define FINS_SENSADDR 0x60 /* sensors assigned I/O address (2 bytes) */ 68 69 /* in sensors space */ 70 #define FINS_TMODE 0x01 /* temperature mode reg */ 71 72 #define FINS_MAX_SENSORS 20 73 /* 74 * Fintek chips typically measure voltages using 8mv steps. 75 * To measure higher voltages the input is attenuated with (external) 76 * resistors. Negative voltages are measured using inverting op amps 77 * and resistors. So we have to convert the sensor values back to 78 * real voltages by applying the appropriate resistor factor. 79 */ 80 #define FRFACT_NONE 8000 81 #define FRFACT(x, y) (FRFACT_NONE * ((x) + (y)) / (y)) 82 #define FNRFACT(x, y) (-FRFACT_NONE * (x) / (y)) 83 84 #if defined(FINSDEBUG) 85 #define DPRINTF(x) do { printf x; } while (0) 86 #else 87 #define DPRINTF(x) 88 #endif 89 90 struct fins_softc; 91 92 struct fins_sensor { 93 char *fs_desc; 94 enum sensor_type fs_type; 95 u_int8_t fs_aux; 96 u_int8_t fs_reg; 97 void (*fs_refresh)(struct fins_softc *, int); 98 int fs_rfact; 99 }; 100 101 struct fins_softc { 102 struct device sc_dev; 103 104 struct ksensor fins_ksensors[FINS_MAX_SENSORS]; 105 struct ksensordev fins_sensordev; 106 struct sensor_task *fins_sensortask; 107 struct fins_sensor *fins_sensors; 108 u_int fins_numsensors; 109 void (*refresh_sensor_data) (struct fins_softc *); 110 111 u_int8_t (*fins_readreg)(struct fins_softc *, int); 112 void (*fins_writereg)(struct fins_softc *, int, int); 113 u_int fins_tempsel; 114 }; 115 116 117 struct fins_isa_softc { 118 struct fins_softc sc_finssc; 119 120 bus_space_tag_t sc_iot; 121 bus_space_handle_t sc_ioh; 122 }; 123 124 int fins_isa_match(struct device *, void *, void *); 125 void fins_isa_attach(struct device *, struct device *, void *); 126 u_int8_t fins_isa_readreg(struct fins_softc *, int); 127 void fins_isa_writereg(struct fins_softc *, int, int); 128 129 void fins_setup_sensors(struct fins_softc *, struct fins_sensor *); 130 void fins_refresh(void *); 131 132 void fins_refresh_volt(struct fins_softc *, int); 133 void fins_refresh_temp(struct fins_softc *, int); 134 void fins_refresh_offset(struct fins_softc *, int); 135 void fins_refresh_fanrpm(struct fins_softc *, int); 136 void fins_attach(struct fins_softc *); 137 int fins_detach(struct fins_softc *); 138 139 struct cfattach fins_ca = { 140 sizeof(struct fins_isa_softc), 141 fins_isa_match, 142 fins_isa_attach 143 }; 144 145 struct cfdriver fins_cd = { 146 NULL, "fins", DV_DULL 147 }; 148 149 struct fins_sensor fins_sensors[] = { 150 /* Voltage */ 151 { "+3.3V", SENSOR_VOLTS_DC, 0, 0x10, fins_refresh_volt, FRFACT(100, 100) }, 152 { "Vtt", SENSOR_VOLTS_DC, 0, 0x11, fins_refresh_volt, FRFACT_NONE }, 153 { "Vram", SENSOR_VOLTS_DC, 0, 0x12, fins_refresh_volt, FRFACT(100, 100) }, 154 { "Vchips", SENSOR_VOLTS_DC, 0, 0x13, fins_refresh_volt, FRFACT(47, 100) }, 155 { "+5V", SENSOR_VOLTS_DC, 0, 0x14, fins_refresh_volt, FRFACT(200, 47) }, 156 { "+12V", SENSOR_VOLTS_DC, 0, 0x15, fins_refresh_volt, FRFACT(200, 20) }, 157 { "Vcc 1.5V", SENSOR_VOLTS_DC, 0, 0x16, fins_refresh_volt, FRFACT_NONE }, 158 { "VCore", SENSOR_VOLTS_DC, 0, 0x17, fins_refresh_volt, FRFACT_NONE }, 159 { "Vsb", SENSOR_VOLTS_DC, 0, 0x18, fins_refresh_volt, FRFACT(200, 47) }, 160 { "Vsbint", SENSOR_VOLTS_DC, 0, 0x19, fins_refresh_volt, FRFACT(200, 47) }, 161 { "Vbat", SENSOR_VOLTS_DC, 0, 0x1A, fins_refresh_volt, FRFACT(200, 47) }, 162 163 /* Temperature */ 164 { "Temp1", SENSOR_TEMP, 0x01, 0x1B, fins_refresh_temp }, 165 { "Temp2", SENSOR_TEMP, 0x02, 0x1C, fins_refresh_temp }, 166 { "Temp3", SENSOR_TEMP, 0x04, 0x1D, fins_refresh_temp }, 167 168 /* Fans */ 169 { "Fan1", SENSOR_FANRPM, 0, 0x20, fins_refresh_fanrpm }, 170 { "Fan2", SENSOR_FANRPM, 0, 0x22, fins_refresh_fanrpm }, 171 { "Fan3", SENSOR_FANRPM, 0, 0x24, fins_refresh_fanrpm }, 172 173 { NULL } 174 }; 175 176 177 int 178 fins_isa_match(struct device *parent, void *match, void *aux) 179 { 180 bus_space_tag_t iot; 181 bus_addr_t iobase; 182 bus_space_handle_t ioh; 183 struct isa_attach_args *ia = aux; 184 u_int val; 185 186 iot = ia->ia_iot; 187 iobase = ia->ipa_io[0].base; 188 189 if (bus_space_map(iot, iobase, 2, 0, &ioh)) 190 return (0); 191 192 /* Fintek uses magic cookie locks to distinguish their chips */ 193 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK); 194 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK); 195 bus_space_write_1(iot, ioh, 0, FINS_FUNC_SEL); 196 bus_space_write_1(iot, ioh, 1, 0); /* IDs appear only in space 0 */ 197 bus_space_write_1(iot, ioh, 0, FINS_MANUF); 198 val = bus_space_read_1(iot, ioh, 1) << 8; 199 bus_space_write_1(iot, ioh, 0, FINS_MANUF + 1); 200 val |= bus_space_read_1(iot, ioh, 1); 201 if (val != FINTEK_ID) { 202 bus_space_write_1(iot, ioh, 0, FINS_LOCK); 203 goto notfound; 204 } 205 bus_space_write_1(iot, ioh, 0, FINS_CHIP); 206 val = bus_space_read_1(iot, ioh, 1) << 8; 207 bus_space_write_1(iot, ioh, 0, FINS_CHIP + 1); 208 val |= bus_space_read_1(iot, ioh, 1); 209 /* If we cared which Fintek chip this was we would save the chip ID here */ 210 if (val != FINS_ID) { 211 bus_space_write_1(iot, ioh, 0, FINS_LOCK); 212 goto notfound; 213 } 214 /* select sensor function of the chip */ 215 bus_space_write_1(iot, ioh, FINS_ADDR, FINS_FUNC_SEL); 216 bus_space_write_1(iot, ioh, FINS_DATA, FINS_FUNC_SENSORS); 217 /* read I/O address assigned by BIOS to this function */ 218 bus_space_write_1(iot, ioh, FINS_ADDR, FINS_SENSADDR); 219 val = bus_space_read_1(iot, ioh, FINS_DATA) << 8; 220 bus_space_write_1(iot, ioh, FINS_ADDR, FINS_SENSADDR + 1); 221 val |= bus_space_read_1(iot, ioh, FINS_DATA); 222 bus_space_write_1(iot, ioh, 0, FINS_LOCK); 223 ia->ipa_io[1].length = 2; 224 ia->ipa_io[1].base = val; 225 226 bus_space_unmap(iot, ioh, 2); 227 ia->ipa_nio = 2; 228 ia->ipa_io[0].length = 2; 229 ia->ipa_nmem = 0; 230 ia->ipa_nirq = 0; 231 ia->ipa_ndrq = 0; 232 return (1); 233 234 notfound: 235 bus_space_unmap(iot, ioh, 2); 236 return (0); 237 } 238 239 void 240 fins_isa_attach(struct device *parent, struct device *self, void *aux) 241 { 242 struct fins_isa_softc *sc = (struct fins_isa_softc *)self; 243 struct isa_attach_args *ia = aux; 244 bus_addr_t iobase; 245 246 sc->sc_iot = ia->ia_iot; 247 iobase = ia->ipa_io[1].base; 248 sc->sc_finssc.fins_writereg = fins_isa_writereg; 249 sc->sc_finssc.fins_readreg = fins_isa_readreg; 250 if (bus_space_map(sc->sc_iot, iobase, 2, 0, &sc->sc_ioh)) { 251 printf(": can't map i/o space\n"); 252 return; 253 } 254 fins_attach(&sc->sc_finssc); 255 } 256 257 u_int8_t 258 fins_isa_readreg(struct fins_softc *lmsc, int reg) 259 { 260 struct fins_isa_softc *sc = (struct fins_isa_softc *)lmsc; 261 262 bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_ADDR, reg); 263 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, FINS_DATA)); 264 } 265 266 void 267 fins_isa_writereg(struct fins_softc *lmsc, int reg, int val) 268 { 269 struct fins_isa_softc *sc = (struct fins_isa_softc *)lmsc; 270 271 bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_ADDR, reg); 272 bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_DATA, val); 273 } 274 275 void 276 fins_attach(struct fins_softc *sc) 277 { 278 u_int i; 279 280 fins_setup_sensors(sc, fins_sensors); 281 sc->fins_sensortask = sensor_task_register(sc, fins_refresh, 5); 282 if (sc->fins_sensortask == NULL) { 283 printf(": unable to register update task\n"); 284 return; 285 } 286 287 printf("\n"); 288 /* Add sensors */ 289 for (i = 0; i < sc->fins_numsensors; ++i) 290 sensor_attach(&sc->fins_sensordev, &sc->fins_ksensors[i]); 291 sensordev_install(&sc->fins_sensordev); 292 } 293 294 int 295 fins_detach(struct fins_softc *sc) 296 { 297 int i; 298 299 /* Remove sensors */ 300 sensordev_deinstall(&sc->fins_sensordev); 301 for (i = 0; i < sc->fins_numsensors; i++) 302 sensor_detach(&sc->fins_sensordev, &sc->fins_ksensors[i]); 303 304 if (sc->fins_sensortask != NULL) 305 sensor_task_unregister(sc->fins_sensortask); 306 307 return 0; 308 } 309 310 311 void 312 fins_setup_sensors(struct fins_softc *sc, struct fins_sensor *sensors) 313 { 314 int i; 315 struct ksensor *ksensor = sc->fins_ksensors; 316 317 strlcpy(sc->fins_sensordev.xname, sc->sc_dev.dv_xname, 318 sizeof(sc->fins_sensordev.xname)); 319 320 for (i = 0; sensors[i].fs_desc; i++) { 321 ksensor[i].type = sensors[i].fs_type; 322 strlcpy(ksensor[i].desc, sensors[i].fs_desc, 323 sizeof(ksensor[i].desc)); 324 } 325 sc->fins_numsensors = i; 326 sc->fins_sensors = sensors; 327 sc->fins_tempsel = sc->fins_readreg(sc, FINS_TMODE); 328 } 329 330 void 331 fins_refresh(void *arg) 332 { 333 struct fins_softc *sc = arg; 334 int i; 335 336 for (i = 0; i < sc->fins_numsensors; i++) 337 sc->fins_sensors[i].fs_refresh(sc, i); 338 } 339 340 void 341 fins_refresh_volt(struct fins_softc *sc, int n) 342 { 343 struct ksensor *sensor = &sc->fins_ksensors[n]; 344 struct fins_sensor *fs = &sc->fins_sensors[n]; 345 int data; 346 347 data = sc->fins_readreg(sc, fs->fs_reg); 348 if (data == 0xff || data == 0) { 349 sensor->flags |= SENSOR_FINVALID; 350 sensor->value = 0; 351 } else { 352 sensor->flags &= ~SENSOR_FINVALID; 353 sensor->value = data * fs->fs_rfact; 354 } 355 } 356 357 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */ 358 void 359 fins_refresh_temp(struct fins_softc *sc, int n) 360 { 361 struct ksensor *sensor = &sc->fins_ksensors[n]; 362 struct fins_sensor *fs = &sc->fins_sensors[n]; 363 u_int data; 364 u_int max; 365 366 /* 367 * The data sheet says that the range of the temperature 368 * sensor is between 0 and 127 or 140 degrees C depending on 369 * what kind of sensor is used. 370 * A disconnected sensor seems to read over 110 or so. 371 */ 372 data = sc->fins_readreg(sc, fs->fs_reg) & 0xFF; 373 max = (sc->fins_tempsel & fs->fs_aux) ? 111 : 128; 374 if (data == 0 || data >= max) { /* disconnected? */ 375 sensor->flags |= SENSOR_FINVALID; 376 sensor->value = 0; 377 } else { 378 sensor->flags &= ~SENSOR_FINVALID; 379 sensor->value = data * 1000000 + 273150000; 380 } 381 } 382 383 /* The chip holds a fudge factor for BJT sensors */ 384 /* this is currently unused but might be reenabled */ 385 void 386 fins_refresh_offset(struct fins_softc *sc, int n) 387 { 388 struct ksensor *sensor = &sc->fins_ksensors[n]; 389 struct fins_sensor *fs = &sc->fins_sensors[n]; 390 u_int data; 391 392 sensor->flags &= ~SENSOR_FINVALID; 393 data = sc->fins_readreg(sc, fs->fs_reg); 394 data |= ~0 * (data & 0x40); /* sign extend 7-bit value */ 395 sensor->value = data * 1000000 + 273150000; 396 } 397 398 399 /* fan speed appears to be a 12-bit number */ 400 void 401 fins_refresh_fanrpm(struct fins_softc *sc, int n) 402 { 403 struct ksensor *sensor = &sc->fins_ksensors[n]; 404 struct fins_sensor *fs = &sc->fins_sensors[n]; 405 int data; 406 407 data = sc->fins_readreg(sc, fs->fs_reg) << 8; 408 data |= sc->fins_readreg(sc, fs->fs_reg + 1); 409 if (data >= 0xfff) { 410 sensor->value = 0; 411 sensor->flags |= SENSOR_FINVALID; 412 } else { 413 sensor->value = 1500000 / data; 414 sensor->flags &= ~SENSOR_FINVALID; 415 } 416 } 417