#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <bus/isa/isavar.h>
#include <bus/isa/isa_common.h>
#include "wbsioreg.h"
#include "wbsiovar.h"
static void wbsio_identify(driver_t *, device_t);
static int wbsio_probe(device_t);
static int wbsio_attach(device_t);
static int wbsio_detach(device_t);
static device_method_t wbsio_methods[] = {
DEVMETHOD(device_identify, wbsio_identify),
DEVMETHOD(device_probe, wbsio_probe),
DEVMETHOD(device_attach, wbsio_attach),
DEVMETHOD(device_detach, wbsio_detach),
DEVMETHOD(bus_add_child, bus_generic_add_child),
DEVMETHOD(bus_set_resource, bus_generic_set_resource),
DEVMETHOD(bus_alloc_resource, isa_alloc_resource),
DEVMETHOD(bus_release_resource, isa_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD_END
};
static driver_t wbsio_driver = {
"wbsio",
wbsio_methods,
sizeof(struct wbsio_softc)
};
static devclass_t wbsio_devclass;
DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL);
static __inline void
wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
{
bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
}
static __inline void
wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
{
bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
}
static __inline u_int8_t
wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
{
bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
return (bus_space_read_1(iot, ioh, WBSIO_DATA));
}
static __inline void
wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
u_int8_t data)
{
bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
bus_space_write_1(iot, ioh, WBSIO_DATA, data);
}
static void
wbsio_identify(driver_t *driver, device_t parent)
{
#ifdef KLD_MODULE
device_t child[2];
const int port[2] = { 0x2e, 0x4e };
for (int i = 0; i < 2; i++) {
child[i] = device_find_child(parent, driver->name, i);
if (child[i] == NULL) {
child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
driver->name, i);
if (child[i] == NULL) {
kprintf("%s: cannot add child[%i]\n",
__func__, i);
continue;
}
} else
continue;
if (bus_set_resource(child[i], SYS_RES_IOPORT, 0,
port[i], WBSIO_IOSIZE, -1))
kprintf("%s: cannot set resource for child[%i]\n",
__func__, i);
}
#endif
}
static int
wbsio_probe(device_t dev)
{
struct resource *iores;
int iorid = 0;
bus_space_tag_t iot;
bus_space_handle_t ioh;
uint8_t reg_id, reg_rev;
const char *desc = NULL;
char fulldesc[64];
iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
0ul, ~0ul, WBSIO_IOSIZE,
RF_ACTIVE);
if (iores == NULL)
return ENXIO;
iot = rman_get_bustag(iores);
ioh = rman_get_bushandle(iores);
wbsio_conf_enable(iot, ioh);
reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID);
reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV);
wbsio_conf_disable(iot, ioh);
bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
switch (reg_id) {
case WBSIO_ID_W83627HF:
desc = "W83627HF";
break;
case WBSIO_ID_W83627THF:
desc = "W83627THF";
break;
case WBSIO_ID_W83627EHF:
desc = "W83627EHF";
break;
case WBSIO_ID_W83627DHG:
desc = "W83627DHG";
break;
case WBSIO_ID_W83627DHGP:
desc = "W83627DHG-P";
break;
case WBSIO_ID_W83627UHG:
desc = "W83627UHG";
break;
case WBSIO_ID_W83637HF:
desc = "W83637HF";
break;
case WBSIO_ID_W83667HG:
desc = "W83667HG";
break;
case WBSIO_ID_W83687THF:
desc = "W83687THF";
break;
case WBSIO_ID_W83697HF:
desc = "W83697HF";
break;
case WBSIO_ID_NCT6776F:
desc = "NCT6776F";
break;
}
if (desc == NULL) {
#ifndef KLD_MODULE
if (bootverbose)
#endif
if (!(reg_id == 0xff && reg_rev == 0xff))
device_printf(dev, "%s port 0x%02x: "
"Device ID 0x%02x, Rev 0x%02x\n",
__func__, isa_get_port(dev),
reg_id, reg_rev);
return ENXIO;
}
ksnprintf(fulldesc, sizeof(fulldesc),
"Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev);
device_set_desc_copy(dev, fulldesc);
return 0;
}
static int
wbsio_attach(device_t dev)
{
struct wbsio_softc *sc = device_get_softc(dev);
uint8_t reg0, reg1;
uint16_t iobase;
device_t child;
sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
0ul, ~0ul, WBSIO_IOSIZE,
RF_ACTIVE);
if (sc->sc_iores == NULL) {
device_printf(dev, "can't map i/o space\n");
return ENXIO;
}
sc->sc_iot = rman_get_bustag(sc->sc_iores);
sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
sc->sc_devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID);
wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
iobase = (reg1 << 8) | (reg0 & ~0x7);
device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase);
wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
if (iobase == 0) {
device_printf(dev, "no hardware monitor configured\n");
return 0;
}
child = BUS_ADD_CHILD(dev, dev, 0, "lm", -1);
if (child == NULL) {
device_printf(dev, "cannot add child\n");
return ENXIO;
}
if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8, -1)) {
device_printf(dev, "cannot set resource\n");
return ENXIO;
}
return device_probe_and_attach(child);
}
static int
wbsio_detach(device_t dev)
{
struct wbsio_softc *sc = device_get_softc(dev);
return bus_release_resource(dev, SYS_RES_IOPORT,
sc->sc_iorid, sc->sc_iores);
}