FreeBSD/sys/dev/wbwd/wbwd.c
/*
 * Probe/attach the Winbond Super I/O chip.
 *
 * Initial abstraction to possibly support more chips:
 * - Iterate over the well known base ports, try to enable extended function
 *   mode and read and match the device ID and device revision.  Unfortunately
 *   the Vendor ID is in the hardware monitoring section accessible by different
 *   base ports only.
 * - Also HEFRAS, which would tell use the base port, is only accessible after
 *   entering extended function mode, for which the base port is needed.
 *   At least check HEFRAS to match the current base port we are probing.
 * - On match set the description, remember functions to enter/exit extended
 *   function mode as well as the base port.
 */
Probe/attach the Winbond Super I/O chip.
Initial abstraction to possibly support more chips:
- Iterate over the well known base ports, try to enable extended function
mode and read and match the device ID and device revision.  Unfortunately
the Vendor ID is in the hardware monitoring section accessible by different
base ports only.
- Also HEFRAS, which would tell use the base port, is only accessible after
entering extended function mode, for which the base port is needed.
At least check HEFRAS to match the current base port we are probing.
- On match set the description, remember functions to enter/exit extended
function mode as well as the base port.
Probe / attach the Winbond Super I / O chip . Initial abstraction to possibly support more chips : - Iterate over the well known base ports try to enable extended function mode and read and match the device ID and device revision . Unfortunately the Vendor ID is in the hardware monitoring section accessible by different base ports only . - Also HEFRAS which would tell use the base port is only accessible after entering extended function mode for which the base port is needed . At least check HEFRAS to match the current base port we are probing . - On match set the description remember functions to enter / exit extended function mode as well as the base port .
static int
wb_probe_enable(device_t dev, int probe)
{
	struct wb_softc *sc;
	int error, found, i, j;
	uint8_t dev_id, dev_rev, cr26;
	char buf[128];
	if (dev == NULL)
		sc = NULL;
	else {
		sc = device_get_softc(dev);
		bzero(sc, sizeof(*sc));
		sc->dev = dev;
	}
	error = ENXIO;
	found = 0;
	for (i = 0; i < nitems(probe_addrs); i++) {
		if (sc != NULL) {
			sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
			    probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
			if (sc->portres == NULL)
				continue;
			sc->bst = rman_get_bustag(sc->portres);
			sc->bsh = rman_get_bushandle(sc->portres);
		}
		error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer);
		if (error != 0)
			goto cleanup;
		write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG);
		dev_id = read_efdr_1(sc, probe_addrs[i].efer);
		write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG);
		dev_rev = read_efdr_1(sc, probe_addrs[i].efer);
		write_efir_1(sc, probe_addrs[i].efer, WB_CR26);
		cr26 = read_efdr_1(sc, probe_addrs[i].efer);
		if (dev_id == 0xff && dev_rev == 0xff)
			goto cleanup;
		if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
		    ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
			if (dev != NULL)
				device_printf(dev, "HEFRAS and EFER do not "
				    "align: EFER 0x%02x DevID 0x%02x DevRev "
				    "0x%02x CR26 0x%02x\n",
				    probe_addrs[i].efer, dev_id, dev_rev, cr26);
			goto cleanup;
		}
		for (j = 0; j < nitems(wb_devs); j++) {
			if (wb_devs[j].device_id == dev_id) {
				found = 1;
				break;
			}
		}
		if (probe && dev != NULL) {
			snprintf(buf, sizeof(buf),
			    "%s (0x%02x/0x%02x) Watchdog Timer",
			    found ? wb_devs[j].descr :
			     "Unknown Winbond/Nuvoton", dev_id, dev_rev);
			device_set_desc_copy(dev, buf);
		}
		if (dev != NULL && !found) {
			found = 1;
			j = 0;
		}
cleanup:
		if (probe || !found) {
			(*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer);
			if (sc != NULL)
				(void) bus_release_resource(dev, SYS_RES_IOPORT,
				    sc->rid, sc->portres);
		}
		if (found) {
			if (sc != NULL) {
				sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
				sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
				sc->chip = wb_devs[j].chip;
				sc->ctl_reg = 0xf5;
				sc->time_reg = 0xf6;
				sc->csr_reg = 0xf7;
				if (sc->chip == w83697hf ||
				    sc->chip == w83697ug) {
					sc->ctl_reg = 0xf3;
					sc->time_reg = 0xf4;
				} else if (sc->chip == nct6102) {
					sc->ctl_reg = 0xf0;
					sc->time_reg = 0xf1;
					sc->csr_reg = 0xf2;
				}
			}
			return (BUS_PROBE_SPECIFIC);
		} else
			error = ENXIO;
	}
	return (error);
}
static int wb_probe_enable ( device_t dev , int probe ) { struct wb_softc * sc ; int error , found , i , j ; uint8_t dev_id , dev_rev , cr26 ; char buf [ 128 ] ; if ( dev == NULL ) sc = NULL ; else { sc = device_get_softc ( dev ) ; bzero ( sc , sizeof ( * sc ) ) ; sc -> dev = dev ; } error = ENXIO ; found = 0 ; for ( i = 0 ; i < nitems ( probe_addrs ) ; i ++ ) { if ( sc != NULL ) { sc -> portres = bus_alloc_resource ( dev , SYS_RES_IOPORT , & sc -> rid , probe_addrs [ i ] . efer , probe_addrs [ i ] . efer + 1 , 2 , RF_ACTIVE ) ; if ( sc -> portres == NULL ) continue ; sc -> bst = rman_get_bustag ( sc -> portres ) ; sc -> bsh = rman_get_bushandle ( sc -> portres ) ; } error = ( * probe_addrs [ i ] . ext_cfg_enter_f ) ( sc , probe_addrs [ i ] . efer ) ; if ( error != 0 ) goto cleanup ; write_efir_1 ( sc , probe_addrs [ i ] . efer , WB_DEVICE_ID_REG ) ; dev_id = read_efdr_1 ( sc , probe_addrs [ i ] . efer ) ; write_efir_1 ( sc , probe_addrs [ i ] . efer , WB_DEVICE_REV_REG ) ; dev_rev = read_efdr_1 ( sc , probe_addrs [ i ] . efer ) ; write_efir_1 ( sc , probe_addrs [ i ] . efer , WB_CR26 ) ; cr26 = read_efdr_1 ( sc , probe_addrs [ i ] . efer ) ; if ( dev_id == 0xff && dev_rev == 0xff ) goto cleanup ; if ( ( ( cr26 & 0x40 ) == 0x00 && probe_addrs [ i ] . efer != 0x2e ) || ( ( cr26 & 0x40 ) == 0x40 && probe_addrs [ i ] . efer != 0x4e ) ) { if ( dev != NULL ) device_printf ( dev , " " " " " \n " , probe_addrs [ i ] . efer , dev_id , dev_rev , cr26 ) ; goto cleanup ; } for ( j = 0 ; j < nitems ( wb_devs ) ; j ++ ) { if ( wb_devs [ j ] . device_id == dev_id ) { found = 1 ; break ; } } if ( probe && dev != NULL ) { snprintf ( buf , sizeof ( buf ) , " " , found ? wb_devs [ j ] . descr : " " , dev_id , dev_rev ) ; device_set_desc_copy ( dev , buf ) ; } if ( dev != NULL && ! found ) { found = 1 ; j = 0 ; } cleanup : if ( probe || ! found ) { ( * probe_addrs [ i ] . ext_cfg_exit_f ) ( sc , probe_addrs [ i ] . efer ) ; if ( sc != NULL ) ( void ) bus_release_resource ( dev , SYS_RES_IOPORT , sc -> rid , sc -> portres ) ; } if ( found ) { if ( sc != NULL ) { sc -> ext_cfg_enter_f = probe_addrs [ i ] . ext_cfg_enter_f ; sc -> ext_cfg_exit_f = probe_addrs [ i ] . ext_cfg_exit_f ; sc -> chip = wb_devs [ j ] . chip ; sc -> ctl_reg = 0xf5 ; sc -> time_reg = 0xf6 ; sc -> csr_reg = 0xf7 ; if ( sc -> chip == w83697hf || sc -> chip == w83697ug ) { sc -> ctl_reg = 0xf3 ; sc -> time_reg = 0xf4 ; } else if ( sc -> chip == nct6102 ) { sc -> ctl_reg = 0xf0 ; sc -> time_reg = 0xf1 ; sc -> csr_reg = 0xf2 ; } } return ( BUS_PROBE_SPECIFIC ) ; } else error = ENXIO ; } return ( error ) ; }
Probe/attach the Winbond Super I/O chip.
Probe / attach the Winbond Super I / O chip .
/* Allocate bus resources for IO index/data register access. */ /* Identify the SuperIO chip. */ /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ /* If this is hinted attach, try to guess the model. */ /*
		 * Stop probing if have successfully identified the SuperIO.
		 * Remember the extended function mode enter/exit functions
		 * for operations.
		 */