1 /* $OpenBSD: fins.c,v 1.1 2008/03/19 19:33:09 deraadt Exp $ */
2 /* $NetBSD: finsio_isa.c,v 1.3 2008/04/05 18:32:14 xtraeme Exp $ */
5 * Copyright (c) 2008 Juan Romero Pardines
6 * Copyright (c) 2007, 2008 Geoff Steckel
7 * Copyright (c) 2005, 2006 Mark Kettenis
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/cdefs.h>
22 __KERNEL_RCSID(0, "$NetBSD: finsio_isa.c,v 1.3 2008/04/05 18:32:14 xtraeme Exp $");
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/device.h>
29 #include <dev/isa/isareg.h>
30 #include <dev/isa/isavar.h>
32 #include <dev/sysmon/sysmonvar.h>
34 /* Derived from LM78 code. Only handles chips attached to ISA bus */
37 * Fintek F71805/F71883 Super I/O datasheets:
38 * http://www.fintek.com.tw/files/productfiles/F71805F_V025.pdf
39 * http://www.fintek.com.tw/files/productfiles/F71883_V026P.pdf
41 * This chip is a multi-io chip with many functions.
42 * Each function may be relocated in I/O space by the BIOS.
43 * The base address (2E or 4E) accesses a configuration space which
44 * has pointers to the individual functions. The config space must be
45 * unlocked with a cookie and relocked afterwards. The chip ID is stored
46 * in config space so it is not normally visible.
48 * The voltage dividers specified are from reading the chips on one board.
49 * There is no way to determine what they are in the general case.
52 #define FINSIO_UNLOCK 0x87 /* magic constant - write 2x to select chip */
53 #define FINSIO_LOCK 0xaa /* magic constant - write 1x to deselect reg */
55 #define FINSIO_FUNC_SEL 0x07 /* select which subchip to access */
56 # define FINSIO_FUNC_HWMON 0x4
58 /* ISA registers index to an internal register space on chip */
59 #define FINSIO_ADDR 0 /* global configuration index registers */
63 * The F71882/F71883 chips use a different Hardware Monitor
66 #define FINSIO_F71882_HWM_OFFSET 5
68 /* Global configuration registers */
69 #define FINSIO_MANUF 0x23 /* manufacturer ID */
70 # define FINTEK_ID 0x1934
71 #define FINSIO_CHIP 0x20 /* chip ID */
72 # define FINSIO_IDF71805 0x0406
73 # define FINSIO_IDF71806 0x0341 /* F71872 and F1806 F/FG */
74 # define FINSIO_IDF71883 0x0541 /* F71882 and F1883 */
75 # define FINSIO_IDF71862 0x0601 /* F71862FG */
77 /* in bank sensors of config space */
78 #define FINSIO_SENSADDR 0x60 /* sensors assigned I/O address (2 bytes) */
80 #define FINSIO_HWMON_CONF 0x01 /* Hardware Monitor Config. Register */
82 /* in sensors space */
83 #define FINSIO_TMODE 0x01 /* temperature mode reg */
85 #define FINSIO_MAX_SENSORS 20
87 * Fintek chips typically measure voltages using 8mv steps.
88 * To measure higher voltages the input is attenuated with (external)
89 * resistors. Negative voltages are measured using inverting op amps
90 * and resistors. So we have to convert the sensor values back to
91 * real voltages by applying the appropriate resistor factor.
93 #define FRFACT_NONE 8000
94 #define FRFACT(x, y) (FRFACT_NONE * ((x) + (y)) / (y))
95 #define FNRFACT(x, y) (-FRFACT_NONE * (x) / (y))
97 #if defined(FINSIODEBUG)
98 #define DPRINTF(x) do { printf x; } while (0)
103 struct finsio_softc
{
104 bus_space_tag_t sc_iot
;
105 bus_space_handle_t sc_ioh
;
107 struct sysmon_envsys
*sc_sme
;
108 envsys_data_t sc_sensor
[FINSIO_MAX_SENSORS
];
109 struct finsio_sensor
*sc_finsio_sensors
;
114 struct finsio_sensor
{
119 void (*fs_refresh
)(struct finsio_softc
*, envsys_data_t
*);
123 static int finsio_isa_match(device_t
, cfdata_t
, void *);
124 static void finsio_isa_attach(device_t
, device_t
, void *);
125 static int finsio_isa_detach(device_t
, int);
127 static void finsio_enter(bus_space_tag_t
, bus_space_handle_t
);
128 static void finsio_exit(bus_space_tag_t
, bus_space_handle_t
);
129 static uint8_t finsio_readreg(bus_space_tag_t
, bus_space_handle_t
, int);
130 static void finsio_writereg(bus_space_tag_t
, bus_space_handle_t
, int, int);
132 static void finsio_refresh(struct sysmon_envsys
*, envsys_data_t
*);
133 static void finsio_refresh_volt(struct finsio_softc
*, envsys_data_t
*);
134 static void finsio_refresh_temp(struct finsio_softc
*, envsys_data_t
*);
135 static void finsio_refresh_fanrpm(struct finsio_softc
*, envsys_data_t
*);
137 CFATTACH_DECL_NEW(finsio
, sizeof(struct finsio_softc
),
138 finsio_isa_match
, finsio_isa_attach
, finsio_isa_detach
, NULL
);
140 /* Sensors available in F71805/F71806 */
141 static struct finsio_sensor f71805_sensors
[] = {
145 .fs_type
= ENVSYS_SVOLTS_DC
,
148 .fs_refresh
= finsio_refresh_volt
,
149 .fs_rfact
= FRFACT(100, 100)
153 .fs_type
= ENVSYS_SVOLTS_DC
,
156 .fs_refresh
= finsio_refresh_volt
,
157 .fs_rfact
= FRFACT_NONE
161 .fs_type
= ENVSYS_SVOLTS_DC
,
164 .fs_refresh
= finsio_refresh_volt
,
165 .fs_rfact
= FRFACT(100, 100)
169 .fs_type
= ENVSYS_SVOLTS_DC
,
172 .fs_refresh
= finsio_refresh_volt
,
173 .fs_rfact
= FRFACT(47, 100)
177 .fs_type
= ENVSYS_SVOLTS_DC
,
180 .fs_refresh
= finsio_refresh_volt
,
181 .fs_rfact
= FRFACT(200, 47)
185 .fs_type
= ENVSYS_SVOLTS_DC
,
188 .fs_refresh
= finsio_refresh_volt
,
189 .fs_rfact
= FRFACT(200, 20)
192 .fs_desc
= "Vcc 1.5V",
193 .fs_type
= ENVSYS_SVOLTS_DC
,
196 .fs_refresh
= finsio_refresh_volt
,
197 .fs_rfact
= FRFACT_NONE
201 .fs_type
= ENVSYS_SVOLTS_DC
,
204 .fs_refresh
= finsio_refresh_volt
,
205 .fs_rfact
= FRFACT_NONE
209 .fs_type
= ENVSYS_SVOLTS_DC
,
212 .fs_refresh
= finsio_refresh_volt
,
213 .fs_rfact
= FRFACT(200, 47)
217 .fs_type
= ENVSYS_SVOLTS_DC
,
220 .fs_refresh
= finsio_refresh_volt
,
221 .fs_rfact
= FRFACT(200, 47)
225 .fs_type
= ENVSYS_SVOLTS_DC
,
228 .fs_refresh
= finsio_refresh_volt
,
229 .fs_rfact
= FRFACT(200, 47)
234 .fs_type
= ENVSYS_STEMP
,
237 .fs_refresh
= finsio_refresh_temp
,
242 .fs_type
= ENVSYS_STEMP
,
245 .fs_refresh
= finsio_refresh_temp
,
250 .fs_type
= ENVSYS_STEMP
,
253 .fs_refresh
= finsio_refresh_temp
,
259 .fs_type
= ENVSYS_SFANRPM
,
262 .fs_refresh
= finsio_refresh_fanrpm
,
267 .fs_type
= ENVSYS_SFANRPM
,
270 .fs_refresh
= finsio_refresh_fanrpm
,
275 .fs_type
= ENVSYS_SFANRPM
,
278 .fs_refresh
= finsio_refresh_fanrpm
,
285 /* Sensors available in F71862/F71882/F71883 */
286 static struct finsio_sensor f71883_sensors
[] = {
290 .fs_type
= ENVSYS_SVOLTS_DC
,
293 .fs_refresh
= finsio_refresh_volt
,
294 .fs_rfact
= FRFACT(100, 100)
298 .fs_type
= ENVSYS_SVOLTS_DC
,
301 .fs_refresh
= finsio_refresh_volt
,
302 .fs_rfact
= FRFACT_NONE
306 .fs_type
= ENVSYS_SVOLTS_DC
,
309 .fs_refresh
= finsio_refresh_volt
,
310 .fs_rfact
= FRFACT(100, 100)
314 .fs_type
= ENVSYS_SVOLTS_DC
,
317 .fs_refresh
= finsio_refresh_volt
,
318 .fs_rfact
= FRFACT(47, 100)
322 .fs_type
= ENVSYS_SVOLTS_DC
,
325 .fs_refresh
= finsio_refresh_volt
,
326 .fs_rfact
= FRFACT(200, 47)
330 .fs_type
= ENVSYS_SVOLTS_DC
,
333 .fs_refresh
= finsio_refresh_volt
,
334 .fs_rfact
= FRFACT(200, 20)
338 .fs_type
= ENVSYS_SVOLTS_DC
,
341 .fs_refresh
= finsio_refresh_volt
,
342 .fs_rfact
= FRFACT(100, 100)
345 .fs_desc
= "VSB +3.3V",
346 .fs_type
= ENVSYS_SVOLTS_DC
,
349 .fs_refresh
= finsio_refresh_volt
,
350 .fs_rfact
= FRFACT(200, 47)
354 .fs_type
= ENVSYS_SVOLTS_DC
,
357 .fs_refresh
= finsio_refresh_volt
,
358 .fs_rfact
= FRFACT(200, 47)
363 .fs_type
= ENVSYS_STEMP
,
366 .fs_refresh
= finsio_refresh_temp
,
371 .fs_type
= ENVSYS_STEMP
,
374 .fs_refresh
= finsio_refresh_temp
,
379 .fs_type
= ENVSYS_STEMP
,
382 .fs_refresh
= finsio_refresh_temp
,
388 .fs_type
= ENVSYS_SFANRPM
,
391 .fs_refresh
= finsio_refresh_fanrpm
,
396 .fs_type
= ENVSYS_SFANRPM
,
399 .fs_refresh
= finsio_refresh_fanrpm
,
404 .fs_type
= ENVSYS_SFANRPM
,
407 .fs_refresh
= finsio_refresh_fanrpm
,
412 .fs_type
= ENVSYS_SFANRPM
,
415 .fs_refresh
= finsio_refresh_fanrpm
,
423 finsio_isa_match(device_t parent
, cfdata_t match
, void *aux
)
425 struct isa_attach_args
*ia
= aux
;
426 bus_space_handle_t ioh
;
429 /* Must supply an address */
433 if (ISA_DIRECT_CONFIG(ia
))
436 if (ia
->ia_io
[0].ir_addr
== ISA_UNKNOWN_PORT
)
439 if (bus_space_map(ia
->ia_iot
, ia
->ia_io
[0].ir_addr
, 2, 0, &ioh
))
442 finsio_enter(ia
->ia_iot
, ioh
);
443 /* Find out Manufacturer ID */
444 val
= finsio_readreg(ia
->ia_iot
, ioh
, FINSIO_MANUF
) << 8;
445 val
|= finsio_readreg(ia
->ia_iot
, ioh
, FINSIO_MANUF
+ 1);
446 finsio_exit(ia
->ia_iot
, ioh
);
447 bus_space_unmap(ia
->ia_iot
, ioh
, 2);
449 if (val
!= FINTEK_ID
)
453 ia
->ia_io
[0].ir_size
= 2;
462 finsio_isa_attach(device_t parent
, device_t self
, void *aux
)
464 struct finsio_softc
*sc
= device_private(self
);
465 struct isa_attach_args
*ia
= aux
;
466 bus_space_handle_t ioh
;
467 uint16_t hwmon_baddr
, chipid
, cr
;
472 sc
->sc_iot
= ia
->ia_iot
;
474 /* Map Super I/O configuration space */
475 if (bus_space_map(sc
->sc_iot
, ia
->ia_io
[0].ir_addr
, 2, 0, &ioh
)) {
476 aprint_error(": can't map configuration I/O space\n");
480 finsio_enter(sc
->sc_iot
, ioh
);
481 /* Get the Chip ID */
482 chipid
= finsio_readreg(sc
->sc_iot
, ioh
, FINSIO_CHIP
) << 8;
483 chipid
|= finsio_readreg(sc
->sc_iot
, ioh
, FINSIO_CHIP
+ 1);
485 * Select the Hardware Monitor LDN to find out the I/O
488 finsio_writereg(sc
->sc_iot
, ioh
, FINSIO_FUNC_SEL
, FINSIO_FUNC_HWMON
);
489 hwmon_baddr
= finsio_readreg(sc
->sc_iot
, ioh
, FINSIO_SENSADDR
) << 8;
490 hwmon_baddr
|= finsio_readreg(sc
->sc_iot
, ioh
, FINSIO_SENSADDR
+ 1);
491 finsio_exit(sc
->sc_iot
, ioh
);
492 bus_space_unmap(sc
->sc_iot
, ioh
, 2);
495 case FINSIO_IDF71805
:
496 sc
->sc_finsio_sensors
= f71805_sensors
;
497 aprint_normal(": Fintek F71805 Super I/O\n");
499 case FINSIO_IDF71806
:
500 sc
->sc_finsio_sensors
= f71805_sensors
;
501 aprint_normal(": Fintek F71806/F71872 Super I/O\n");
503 case FINSIO_IDF71862
:
504 hwmon_baddr
+= FINSIO_F71882_HWM_OFFSET
;
505 sc
->sc_finsio_sensors
= f71883_sensors
;
506 aprint_normal(": Fintek F71862 Super I/O\n");
508 case FINSIO_IDF71883
:
509 hwmon_baddr
+= FINSIO_F71882_HWM_OFFSET
;
510 sc
->sc_finsio_sensors
= f71883_sensors
;
511 aprint_normal(": Fintek F71882/F71883 Super I/O\n");
515 * Unknown Chip ID, assume the same register layout
516 * than F71805 for now.
518 sc
->sc_finsio_sensors
= f71805_sensors
;
519 aprint_normal(": Fintek Super I/O (unknown chip ID %x)\n",
524 /* Map Hardware Monitor I/O space */
525 if (bus_space_map(sc
->sc_iot
, hwmon_baddr
, 2, 0, &sc
->sc_ioh
)) {
526 aprint_error(": can't map hwmon I/O space\n");
531 * Enable Hardware monitoring for fan/temperature and
534 cr
= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, FINSIO_HWMON_CONF
);
535 finsio_writereg(sc
->sc_iot
, sc
->sc_ioh
, FINSIO_HWMON_CONF
, cr
| 0x3);
537 /* Find out the temperature mode */
538 sc
->sc_tempsel
= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, FINSIO_TMODE
);
541 * Initialize and attach sensors with sysmon_envsys(9).
543 sc
->sc_sme
= sysmon_envsys_create();
544 for (i
= 0; sc
->sc_finsio_sensors
[i
].fs_desc
; i
++) {
545 sc
->sc_sensor
[i
].units
= sc
->sc_finsio_sensors
[i
].fs_type
;
546 if (sc
->sc_sensor
[i
].units
== ENVSYS_SVOLTS_DC
)
547 sc
->sc_sensor
[i
].flags
= ENVSYS_FCHANGERFACT
;
548 strlcpy(sc
->sc_sensor
[i
].desc
, sc
->sc_finsio_sensors
[i
].fs_desc
,
549 sizeof(sc
->sc_sensor
[i
].desc
));
550 if (sysmon_envsys_sensor_attach(sc
->sc_sme
,
555 sc
->sc_sme
->sme_name
= device_xname(self
);
556 sc
->sc_sme
->sme_cookie
= sc
;
557 sc
->sc_sme
->sme_refresh
= finsio_refresh
;
558 if ((rv
= sysmon_envsys_register(sc
->sc_sme
))) {
559 aprint_error(": unable to register with sysmon (%d)\n", rv
);
563 aprint_normal_dev(self
,
564 "Hardware Monitor registers at 0x%x\n", hwmon_baddr
);
568 sysmon_envsys_destroy(sc
->sc_sme
);
569 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, 2);
573 finsio_isa_detach(device_t self
, int flags
)
575 struct finsio_softc
*sc
= device_private(self
);
577 sysmon_envsys_unregister(sc
->sc_sme
);
578 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, 2);
582 /* Enter Super I/O configuration mode */
584 finsio_enter(bus_space_tag_t iot
, bus_space_handle_t ioh
)
586 bus_space_write_1(iot
, ioh
, FINSIO_ADDR
, FINSIO_UNLOCK
);
587 bus_space_write_1(iot
, ioh
, FINSIO_ADDR
, FINSIO_UNLOCK
);
590 /* Exit Super I/O configuration mode */
592 finsio_exit(bus_space_tag_t iot
, bus_space_handle_t ioh
)
594 bus_space_write_1(iot
, ioh
, FINSIO_ADDR
, FINSIO_LOCK
);
598 finsio_readreg(bus_space_tag_t iot
, bus_space_handle_t ioh
, int reg
)
600 bus_space_write_1(iot
, ioh
, FINSIO_ADDR
, reg
);
601 return bus_space_read_1(iot
, ioh
, FINSIO_DATA
);
605 finsio_writereg(bus_space_tag_t iot
, bus_space_handle_t ioh
, int reg
, int val
)
607 bus_space_write_1(iot
, ioh
, FINSIO_ADDR
, reg
);
608 bus_space_write_1(iot
, ioh
, FINSIO_DATA
, val
);
612 finsio_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
614 struct finsio_softc
*sc
= sme
->sme_cookie
;
615 int i
= edata
->sensor
;
617 sc
->sc_finsio_sensors
[i
].fs_refresh(sc
, edata
);
621 finsio_refresh_volt(struct finsio_softc
*sc
, envsys_data_t
*edata
)
623 struct finsio_sensor
*fs
= &sc
->sc_finsio_sensors
[edata
->sensor
];
626 data
= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, fs
->fs_reg
);
627 DPRINTF(("%s: data 0x%x\n", __func__
, data
));
629 if (data
== 0xff || data
== 0)
630 edata
->state
= ENVSYS_SINVALID
;
632 edata
->state
= ENVSYS_SVALID
;
634 edata
->value_cur
= data
* edata
->rfact
;
636 edata
->value_cur
= data
* fs
->fs_rfact
;
640 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */
642 finsio_refresh_temp(struct finsio_softc
*sc
, envsys_data_t
*edata
)
644 struct finsio_sensor
*fs
= &sc
->sc_finsio_sensors
[edata
->sensor
];
649 * The data sheet says that the range of the temperature
650 * sensor is between 0 and 127 or 140 degrees C depending on
651 * what kind of sensor is used.
652 * A disconnected sensor seems to read over 110 or so.
654 data
= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, fs
->fs_reg
) & 0xFF;
655 DPRINTF(("%s: data 0x%x\n", __func__
, data
));
657 llmax
= (sc
->sc_tempsel
& fs
->fs_aux
) ? 111 : 128;
658 if (data
== 0 || data
>= llmax
) /* disconnected? */
659 edata
->state
= ENVSYS_SINVALID
;
661 edata
->state
= ENVSYS_SVALID
;
662 edata
->value_cur
= data
* 1000000 + 273150000;
666 /* fan speed appears to be a 12-bit number */
668 finsio_refresh_fanrpm(struct finsio_softc
*sc
, envsys_data_t
*edata
)
670 struct finsio_sensor
*fs
= &sc
->sc_finsio_sensors
[edata
->sensor
];
673 data
= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, fs
->fs_reg
) << 8;
674 data
|= finsio_readreg(sc
->sc_iot
, sc
->sc_ioh
, fs
->fs_reg
+ 1);
675 DPRINTF(("%s: data 0x%x\n", __func__
, data
));
678 edata
->state
= ENVSYS_SINVALID
;
680 edata
->value_cur
= 1500000 / data
;
681 edata
->state
= ENVSYS_SVALID
;