#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
struct berkwdt_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
int sc_period;
};
int berkwdt_match(struct device *, void *, void *);
void berkwdt_attach(struct device *, struct device *, void *);
int berkwdt_activate(struct device *, int);
void berkwdt_start(struct berkwdt_softc *sc);
void berkwdt_stop(struct berkwdt_softc *sc);
void berkwdt_reload(struct berkwdt_softc *sc);
int berkwdt_send_command(struct berkwdt_softc *sc, u_int8_t cmd, int *val);
int berkwdt_set_timeout(void *, int);
const struct cfattach berkwdt_ca = {
sizeof(struct berkwdt_softc), berkwdt_match, berkwdt_attach,
NULL, berkwdt_activate
};
struct cfdriver berkwdt_cd = {
NULL, "berkwdt", DV_DULL
};
const struct pci_matchid berkwdt_devices[] = {
{ PCI_VENDOR_PIJNENBURG, PCI_PRODUCT_PIJNENBURG_PCWD_PCI }
};
#define PCWD_PCI_RELOAD 0x00
#define PCWD_PCI_CS1 0x01
#define PCWD_PCI_CS2 0x02
#define PCWD_PCI_WDT_DIS 0x03
#define PCWD_PCI_LSB 0x04
#define PCWD_PCI_MSB 0x05
#define PCWD_PCI_CMD 0x06
#define WD_PCI_WTRP 0x01
#define WD_PCI_TTRP 0x04
#define WD_PCI_R2DS 0x40
#define WD_PCI_WDIS 0x10
#define WD_PCI_WRSP 0x40
#define PCI_CMD_TIMEOUT 150
#define CMD_WRITE_WD_TIMEOUT 0x19
int
berkwdt_send_command(struct berkwdt_softc *sc, u_int8_t cmd, int *val)
{
u_int8_t msb;
u_int8_t lsb;
u_int8_t got_response;
int count;
msb = *val / 256;
lsb = *val % 256;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_LSB, lsb);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_MSB, msb);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CMD, cmd);
got_response = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS2);
got_response &= WD_PCI_WRSP;
for (count = 0; count < PCI_CMD_TIMEOUT && !got_response; count++) {
delay(1000);
got_response = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS2);
got_response &= WD_PCI_WRSP;
}
if (got_response) {
lsb = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_LSB);
msb = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_MSB);
*val = (msb << 8) + lsb;
bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CMD);
return 1;
}
return 0;
}
void
berkwdt_start(struct berkwdt_softc *sc)
{
u_int8_t reg;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_WDT_DIS, 0x00);
delay(1000);
reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS2);
if (reg & WD_PCI_WDIS) {
printf("%s: unable to enable\n", sc->sc_dev.dv_xname);
}
}
void
berkwdt_stop(struct berkwdt_softc *sc)
{
u_int8_t reg;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_WDT_DIS, 0xa5);
delay(1000);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_WDT_DIS, 0xa5);
delay(1000);
reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS2);
if (!(reg & WD_PCI_WDIS)) {
printf("%s: unable to disable\n", sc->sc_dev.dv_xname);
}
}
void
berkwdt_reload(struct berkwdt_softc *sc)
{
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_RELOAD, 0x42);
}
int
berkwdt_match(struct device *parent, void *match, void *aux)
{
return (pci_matchbyid((struct pci_attach_args *)aux, berkwdt_devices,
sizeof(berkwdt_devices) / sizeof(berkwdt_devices[0])));
}
void
berkwdt_attach(struct device *parent, struct device *self, void *aux)
{
struct berkwdt_softc *sc = (struct berkwdt_softc *)self;
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
bus_size_t iosize;
u_int8_t reg;
if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0,
&sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0) != 0) {
printf(": couldn't find PCI I/O region\n");
return;
}
reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS1);
if (reg & WD_PCI_WTRP) {
printf(", warning: watchdog triggered");
if (reg & WD_PCI_TTRP)
printf(", overheat detected");
reg &= WD_PCI_R2DS;
reg |= WD_PCI_WTRP;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCWD_PCI_CS1, reg);
}
printf("\n");
berkwdt_stop(sc);
sc->sc_period = 0;
wdog_register(berkwdt_set_timeout, sc);
}
int
berkwdt_activate(struct device *self, int act)
{
switch (act) {
case DVACT_POWERDOWN:
wdog_shutdown(self);
break;
}
return (0);
}
int
berkwdt_set_timeout(void *self, int timeout)
{
struct berkwdt_softc *sc = self;
int new_timeout = timeout;
if (timeout == 0) {
berkwdt_stop(sc);
} else {
if (sc->sc_period != timeout) {
berkwdt_send_command(sc, CMD_WRITE_WD_TIMEOUT,
&new_timeout);
}
if (sc->sc_period == 0) {
berkwdt_start(sc);
berkwdt_reload(sc);
} else {
berkwdt_reload(sc);
}
}
sc->sc_period = timeout;
return (timeout);
}