summaryrefslogtreecommitdiff
path: root/sys/dev/pci/berkwdt.c
diff options
context:
space:
mode:
authorMichael Knudsen <mk@cvs.openbsd.org>2009-04-24 17:52:56 +0000
committerMichael Knudsen <mk@cvs.openbsd.org>2009-04-24 17:52:56 +0000
commit5e447f5682947f6d1215c333e2384b9cb43a0035 (patch)
tree30398c8fa29152460ec896dcc1166bf85db29e36 /sys/dev/pci/berkwdt.c
parent0f0593975f8218b3760a43a95057eaeda2fa7f47 (diff)
Add berkwdt(4), a driver for Berkshire Products PCI PC Watchdog written
by Wim Van Sebroeck. Commented out in GENERIC but Heriberto Molina is getting devices for developers so we can maintain this more easily. Many thanks to both! Man page will follow later. ``don't hold back for man page'' deraadt
Diffstat (limited to 'sys/dev/pci/berkwdt.c')
-rw-r--r--sys/dev/pci/berkwdt.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/sys/dev/pci/berkwdt.c b/sys/dev/pci/berkwdt.c
new file mode 100644
index 00000000000..8c84901e508
--- /dev/null
+++ b/sys/dev/pci/berkwdt.c
@@ -0,0 +1,244 @@
+/* $OpenBSD: berkwdt.c,v 1.1 2009/04/24 17:52:55 mk Exp $ */
+
+/*
+ * Copyright (c) 2009 Wim Van Sebroeck <wim@iguana.be>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Berkshire PCI-PC Watchdog Card Driver
+ * http://www.pcwatchdog.com/
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/proc.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 {
+ /* wdt_dev must be the first item in the struct */
+ struct device berkwdt_dev;
+
+ /* the watchdog's heartbeat */
+ int heartbeat;
+
+ /* device access through bus space */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+};
+
+int berkwdt_match(struct device *, void *, void *);
+void berkwdt_attach(struct device *, struct device *, void *);
+int berkwdt_set_timeout(void *, int);
+
+struct cfattach berkwdt_ca = {
+ sizeof(struct berkwdt_softc), berkwdt_match, berkwdt_attach
+};
+
+struct cfdriver berkwdt_cd = {
+ NULL, "berkwdt", DV_DULL
+};
+
+const struct pci_matchid berkwdt_devices[] = {
+ { PCI_VENDOR_PIJNENBURG, PCI_PRODUCT_PIJNENBURG_PCWD_PCI }
+};
+
+/* PCWD-PCI I/O Port definitions */
+#define PCWD_PCI_RELOAD 0x00 /* Re-trigger */
+#define PCWD_PCI_CS1 0x01 /* Control Status 1 */
+#define PCWD_PCI_CS2 0x02 /* Control Status 2 */
+#define PCWD_PCI_WDT_DIS 0x03 /* Watchdog Disable */
+#define PCWD_PCI_LSB 0x04 /* Command / Response */
+#define PCWD_PCI_MSB 0x05 /* Command/Response LSB */
+#define PCWD_PCI_CMD 0x06 /* Command/Response MSB */
+
+/* Port 1 : Control Status #1 */
+#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */
+#define WD_PCI_TTRP 0x04 /* Temperature Trip status */
+#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temp-trip reset */
+
+/* Port 2 : Control Status #2 */
+#define WD_PCI_WDIS 0x10 /* Watchdog Disable */
+#define WD_PCI_WRSP 0x40 /* Watchdog wrote response */
+
+/*
+ * According to documentation max. time to process a command for the pci
+ * watchdog card is 100 ms, so we give it 150 ms to do its job.
+ */
+#define PCI_CMD_TIMEOUT 150
+
+/* Watchdog's internal commands */
+#define CMD_WRITE_WD_TIMEOUT 0x19
+
+static int
+berkwdt_send_command(struct berkwdt_softc *sc, u_int8_t cmd, int *val)
+{
+ u_int8_t msb = *val / 256;
+ u_int8_t lsb = *val % 256;
+ u_int8_t got_response;
+ int count;
+
+ /* Send command with data (data first!) */
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_LSB, lsb);
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_MSB, msb);
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_CMD, cmd);
+
+ got_response = bus_space_read_1(sc->iot, 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->iot, sc->ioh, PCWD_PCI_CS2);
+ got_response &= WD_PCI_WRSP;
+ }
+
+ if (got_response) {
+ /* read back response */
+ lsb = bus_space_read_1(sc->iot, sc->ioh, PCWD_PCI_LSB);
+ msb = bus_space_read_1(sc->iot, sc->ioh, PCWD_PCI_MSB);
+ *val = (msb << 8) + lsb;
+
+ /* clear WRSP bit */
+ bus_space_read_1(sc->iot, 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->iot, sc->ioh,
+ PCWD_PCI_WDT_DIS, 0x00);
+ delay(1000);
+
+ reg = bus_space_read_1(sc->iot, sc->ioh, PCWD_PCI_CS2);
+ if (reg & WD_PCI_WDIS) {
+ printf("%s: Card did not acknowledge enable attempt\n",
+ sc->berkwdt_dev.dv_xname);
+ }
+}
+
+void
+berkwdt_stop(struct berkwdt_softc *sc)
+{
+ u_int8_t reg;
+
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_WDT_DIS, 0xa5);
+ delay(1000);
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_WDT_DIS, 0xa5);
+ delay(1000);
+
+ reg = bus_space_read_1(sc->iot, sc->ioh, PCWD_PCI_CS2);
+ if (!(reg & WD_PCI_WDIS)) {
+ printf("%s: Card did not acknowledge disable attempt\n",
+ sc->berkwdt_dev.dv_xname);
+ }
+}
+
+void
+berkwdt_reload(struct berkwdt_softc *sc)
+{
+ bus_space_write_1(sc->iot, 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 *const pa = (struct pci_attach_args *)aux;
+ bus_size_t iosize;
+ u_int8_t reg;
+
+ /* retrieve the I/O region (BAR 0) */
+ if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0,
+ &sc->iot, &sc->ioh, NULL, &iosize, 0) != 0) {
+ printf(": couldn't find PCI I/O region\n");
+ return;
+ }
+
+ /* Check for reboot by the card */
+ reg = bus_space_read_1(sc->iot, sc->ioh, PCWD_PCI_CS1);
+ if (reg & WD_PCI_WTRP) {
+ printf(": Previous reset was caused by the Watchdog card");
+
+ if (reg & WD_PCI_TTRP)
+ printf("- Card sensed a CPU Overheat");
+
+ /* clear trip status & LED and keep mode of relay 2 */
+ reg &= WD_PCI_R2DS;
+ reg |= WD_PCI_WTRP;
+ bus_space_write_1(sc->iot, sc->ioh, PCWD_PCI_CS1, reg);
+ }
+
+ printf("\n");
+
+ /*
+ * ensure that the watchdog is disabled
+ */
+ berkwdt_stop(sc);
+ sc->heartbeat = 0;
+
+ /*
+ * register with the watchdog framework
+ */
+ wdog_register(sc, berkwdt_set_timeout);
+}
+
+int
+berkwdt_set_timeout(void *self, int timeout)
+{
+ struct berkwdt_softc *sc = self;
+ int new_timeout = timeout;
+
+ if (timeout == 0) {
+ /* Disable watchdog */
+ berkwdt_stop(sc);
+ } else {
+ if (sc->heartbeat != timeout) {
+ /* Set new timeout */
+ berkwdt_send_command(sc, CMD_WRITE_WD_TIMEOUT,
+ &new_timeout);
+ }
+ if (sc->heartbeat == 0) {
+ /* Enable watchdog */
+ berkwdt_start(sc);
+ berkwdt_reload(sc);
+ } else {
+ /* Reset timer */
+ berkwdt_reload(sc);
+ }
+ }
+ sc->heartbeat = timeout;
+
+ return (timeout);
+}
+