changeset 6:76ce6e6624b8

Make vordog compatible with watchdog(9).
author Thinker K.F. Li <thinker@branda.to>
date Tue, 08 Jul 2008 14:41:47 +0800
parents e59963b503cb
children 5a281ce7453c
files vordog.c
diffstat 1 files changed, 77 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/vordog.c	Tue Jul 08 12:31:29 2008 +0800
+++ b/vordog.c	Tue Jul 08 14:41:47 2008 +0800
@@ -33,6 +33,7 @@
 #include <sys/ioccom.h>
 #include <sys/types.h>
 #include <sys/malloc.h>
+#include <sys/watchdog.h>
 #include "vordog.h"
 
 #if 0
@@ -43,12 +44,15 @@
 #define VD_STATUS 0x841
 #define VD_INITVAL 0x84a
 #define VD_CTR 0x84b
+/* trigger PCIRST */
+#define VD_TIMER_RST 0xc
 
 typedef struct vd_softc {
     struct cdev *_cdev;
     device_t dev;
     int open_cnt;
-    int init_val;
+    u_int init_val;
+    eventhandler_tag wd9_tag;
 } *vd_softc_t;
 
 #define CDEV_2_SOFTC(_cdev) ((_cdev)->si_drv1)
@@ -98,7 +102,6 @@
 static void
 setup_timer(vordog_cfg_t cfg) {
     int val;
-#define VD_TIMER_RST 0xc
 
     val = cfg->init_val & 0xff;
     outb(VD_INITVAL, val);
@@ -107,7 +110,7 @@
 }
 
 static void
-reset_timer(int init_val) {
+reset_timer(void) {
     outb(VD_STATUS, 0xc0);
 }
 
@@ -123,16 +126,17 @@
     int r = 0;
     vd_softc_t sc;
     vordog_cfg_t cfg;
+    static const u_int unit_factors[] = {0, 1, 60, 360};
 
     sc = CDEV_2_SOFTC(dev);
     switch(cmd) {
     case VDCTL_ENABLE:
 	cfg = (vordog_cfg_t)data;
-	sc->init_val = cfg->init_val & 0xff;
+	sc->init_val = (cfg->init_val & 0xff) * unit_factors[cfg->unit];
 	setup_timer(cfg);
 	break;
     case VDCTL_RESET:
-	reset_timer(sc->init_val);
+	reset_timer();
 	break;
     case VDCTL_DISABLE:
 	disable_timer();
@@ -151,6 +155,67 @@
     .d_name = "vordog",
 };
 
+/* Implements watchdog(9) compatible driver. */
+static int
+vordog_wd9_init(int timeout) {
+    int val;
+
+    if(timeout < 0 || timeout > 255)
+	return EINVAL;
+
+    outb(VD_INITVAL, timeout & 0xff);
+    val = 0x80 | (VDT_1S << 4) | VD_TIMER_RST;
+    outb(VD_CTR, val);
+    return 0;
+}
+
+static void
+vordog_wd9_reset(void) {
+    outb(VD_STATUS, 0xc0);
+}
+
+static void
+vordog_wd9_disable(void) {
+    outb(VD_CTR, 0);
+    outb(VD_STATUS, 0xc0);
+}
+
+static void
+vordog_wd9_evh(void *private, u_int cmd, int *error) {
+    vd_softc_t sc;
+    u_int timeout, u;
+    static const int timeouts[] = { 1, 2, 3, 5, 9, 18, 35, 69, 138};
+
+    sc = (vd_softc_t)private;
+
+    u = cmd & WD_INTERVAL;
+    if(u < 31 || u > 39) {
+	vordog_wd9_disable();
+	sc->init_val = 0;
+	return;
+    }
+
+    timeout = timeouts[u - 31];
+    if(timeout != sc->init_val) {
+	vordog_wd9_init(timeout);
+	sc->init_val = timeout;
+    } else {
+	vordog_wd9_reset();
+    }
+}
+
+static void
+vordog_wd9_register(vd_softc_t sc) {
+    sc->wd9_tag = \
+	EVENTHANDLER_REGISTER(watchdog_list, vordog_wd9_evh, sc, 0);
+}
+
+static void
+vordog_wd9_unregister(vd_softc_t sc) {
+    EVENTHANDLER_DEREGISTER(watchdog_list, sc->wd9_tag);
+}
+
+
 /* Following implements a new bus device.
  * It is attached under nexus bus, an on board device as `vordog0'.
  */
@@ -202,10 +267,13 @@
 
     sc->open_cnt = 0;
     sc->init_val = 0;
+    sc->wd9_tag = NULL;
 
     device_set_desc(dev, "Watchdog of Vortex86 SoC");
     device_printf(dev, "<Vortex86 SoC watchdog> at port 0x%x\n", VD_STATUS);
 
+    vordog_wd9_register(sc);
+
     return 0;
 }
 
@@ -217,6 +285,10 @@
     if(sc->open_cnt != 0)
 	return EBUSY;
 
+    disable_timer();
+
+    vordog_wd9_unregister(sc);
+    
     _cdev = sc->_cdev;
 
     destroy_dev(_cdev);