/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Herb Peyerl and Jason Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Device driver for the control space on the Middle Digital, Inc.
* PCI-Weasel serial console board.
*
* Since the other functions of the PCI-Weasel already appear in
* PCI configuration space, we just need to hook up the watchdog
* timer.
*/
static int
weasel_pci_wdog_setmode(struct sysmon_wdog *smw)
{
struct weasel_softc *sc = smw->smw_cookie;
int error = 0;
if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
error = weasel_pci_wdog_disarm(sc);
} else {
if (smw->smw_period == WDOG_PERIOD_DEFAULT)
smw->smw_period = sc->sc_wdog_period;
else if (smw->smw_period != sc->sc_wdog_period) {
/* Can't change the period on the Weasel. */
return (EINVAL);
}
error = weasel_pci_wdog_arm(sc);
weasel_pci_wdog_tickle(smw);
}
return (error);
}
static int
weasel_pci_wdog_tickle(struct sysmon_wdog *smw)
{
struct weasel_softc *sc = smw->smw_cookie;
u_int8_t reg;
int x;
int s;
int error = 0;
s = splhigh();
/*
* first we tickle the watchdog
*/
reg = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_CHALLENGE);
bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_RESPONSE, ~reg);
/*
* then we check to make sure the weasel is still armed. If someone
* has rebooted the weasel for whatever reason (firmware update),
* then the watchdog timer would no longer be armed and we'd be
* servicing nothing. Let the user know that the machine is no
* longer being monitored by the weasel.
*/
if((x = weasel_pci_wdog_query_state(sc)) == -1)
error = EIO;
if (x == 1) {
error = 0;
} else {
printf("%s: Watchdog timer disabled on PC/Weasel! Disarming wdog.\n",
device_xname(sc->sc_dev));
sc->sc_wdog_armed = 0;
sysmon_wdog_setmode(smw, WDOG_MODE_DISARMED, 0);
error = 1;
}
splx(s);
return (error);
}
static int
weasel_pci_wdog_arm(struct weasel_softc *sc)
{
int x;
int s;
int error = 0;
s = splhigh();
if (weasel_issue_command(sc, OS_CMD_WDT_ENABLE)) {
printf("%s: no reply to watchdog enable. Check Weasel \"Allow Watchdog\" setting.\n",
device_xname(sc->sc_dev));
error = EIO;
}
(void)bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
/*
* Ensure that the Weasel thinks it's in the same mode we want it to
* be in. EIO if not.
*/
x = weasel_pci_wdog_query_state(sc);
switch (x) {
case -1:
error = EIO;
break;
case 0:
sc->sc_wdog_armed = 0;
error = EIO;
break;
case 1:
sc->sc_wdog_armed = 1;
error = 0;
break;
}
splx(s);
return(error);
}
static int
weasel_pci_wdog_disarm(struct weasel_softc *sc)
{
int x;
int s;
int error = 0;
/*
* Ensure that the Weasel thinks it's in the same mode we want it to
* be in. EIO if not.
*/
x = weasel_pci_wdog_query_state(sc);
switch (x) {
case -1:
error = EIO;
break;
case 0:
sc->sc_wdog_armed = 0;
error = 0;
break;
case 1:
sc->sc_wdog_armed = 1;
error = EIO;
break;
}
splx(s);
return(error);
}
static int
weasel_pci_wdog_query_state(struct weasel_softc *sc)
{
u_int8_t v;
if (weasel_issue_command(sc, OS_CMD_WDT_QUERY)) {
printf("%s: didn't reply to watchdog state query.\n",
device_xname(sc->sc_dev));
bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
return(-1);
}
v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
return(v);
}