diff vordog.c @ 0:71475f5afa92

Watchdog for Vortex86 6071 on FreeBSD
author Thinker K.F. Li <thinker@branda.to>
date Tue, 08 Jul 2008 09:57:54 +0800
parents
children 0b9854adb86c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vordog.c	Tue Jul 08 09:57:54 2008 +0800
@@ -0,0 +1,275 @@
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include "vordog.h"
+
+#if 1
+#undef __FreeBSD_version
+#define __FreeBSD_version 800029
+#endif
+
+#define VD_STATUS 0x841
+#define VD_INITVAL 0x84a
+#define VD_CTR 0x84b
+
+typedef struct vd_softc {
+    struct cdev *_cdev;
+    device_t dev;
+    int open_cnt;
+    int init_val;
+} *vd_softc_t;
+
+#define CDEV_2_SOFTC(_cdev) ((_cdev)->si_drv1)
+
+static int
+vordog_open(struct cdev *dev, int oflags, int devtype, struct thread *td) {
+    vd_softc_t sc;
+
+    printf("vordog_open\n");
+    sc = CDEV_2_SOFTC(dev);
+    sc->open_cnt++;
+
+    return 0;
+}
+
+static int
+vordog_close(struct cdev *dev, int fflag, int devtype, struct thread *td) {
+    vd_softc_t sc;
+
+    printf("vordog_close\n");
+    sc = CDEV_2_SOFTC(dev);
+    sc->open_cnt--;
+
+    return 0;
+}
+
+static int
+vordog_go_detach(void) {
+    devclass_t vd_dc;
+    device_t vd_dev, nexus_dev;
+    int r;
+
+    printf("go_detach 1\n");
+    vd_dc = devclass_find("vordog");
+    if(vd_dc == NULL)
+	return EINVAL;
+
+    printf("go_detach 2\n");
+    vd_dev = devclass_get_device(vd_dc, 0);
+    if(vd_dev == NULL)
+	return EINVAL;
+
+    printf("go_detach 3\n");
+    nexus_dev = device_get_parent(vd_dev);
+    if(nexus_dev == NULL)
+	return EINVAL;
+
+    printf("go_detach 4\n");
+    r = device_delete_child(nexus_dev, vd_dev);
+    return r;
+}
+
+static void
+setup_timer(vordog_cfg_t cfg) {
+    int val;
+#define VD_TIMER_RST 0xc
+
+#if 0
+    outb(VD_STATUS, 0xc0);	/* Clear events or it would be reset
+				 * immediately, since it is setted
+				 * by last timeout before PCIRST.
+				 */
+#endif
+    val = cfg->init_val & 0xff;
+    printf("setup_timer 1 (%x)\n", val);
+    pause("vordog setup", hz);
+    outb(VD_INITVAL, val);
+    printf("setup_timer 2\n");
+    val = 0x80 | ((cfg->unit & 0x3) << 4) | VD_TIMER_RST;
+    printf("setup_timer 3 (%x)\n", val);
+    pause("vordog setup", hz);
+    outb(VD_CTR, val);
+    printf("setup_timer 4\n");
+}
+
+static void
+reset_timer(int init_val) {
+#if 0
+    printf("reset_timer 1\n");
+    pause("vordog setup", hz);
+    outb(VD_INITVAL, init_val);
+#endif
+    printf("reset_timer 2\n");
+    pause("vordog setup", hz);
+    outb(VD_STATUS, 0xc0);
+    printf("reset_timer 3\n");
+    pause("vordog setup", hz);
+}
+
+static void
+disable_timer(void) {
+    outb(VD_CTR, 0);
+    outb(VD_STATUS, 0xc0);
+}
+
+static int
+vordog_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+	     int fflag, struct thread *td) {
+    int r = 0;
+    vd_softc_t sc;
+    vordog_cfg_t cfg;
+
+    sc = CDEV_2_SOFTC(dev);
+    switch(cmd) {
+    case VDCTL_ENABLE:
+	printf("VDCTL_ENABLE\n");
+	cfg = (vordog_cfg_t)data;
+	sc->init_val = cfg->init_val & 0xff;
+	setup_timer(cfg);
+	break;
+    case VDCTL_RESET:
+	printf("VDCTL_RESET\n");
+	reset_timer(sc->init_val);
+	break;
+    case VDCTL_DISABLE:
+	printf("VDCTL_DISABLE\n");
+	disable_timer();
+	break;
+    default:
+	r = EINVAL;
+    }
+    return r;
+}
+
+struct cdevsw vordog_cdevsw = {
+    .d_version = D_VERSION,
+    .d_open = vordog_open,
+    .d_close = vordog_close,
+    .d_ioctl = vordog_ioctl,
+    .d_name = "vordog",
+};
+
+static void vordog_identify(driver_t *driver, device_t parent) {
+    device_t vordog;
+    
+    /* make sure vordog device is not in the bus. */
+    vordog = device_find_child(parent, "vordog", 0);
+    if(vordog != NULL) {
+	printf("vordog is still existing!");
+	return;
+    }
+    vordog = BUS_ADD_CHILD(parent, 10, "vordog", 0);
+    printf("vordog_identify\n");
+}
+
+static int vordog_probe(device_t dev) {
+    int b;
+
+    printf("vordog_probe\n");
+    b = inb(VD_INITVAL);
+    printf("init val: %x\n", b);
+    b = inb(VD_CTR);
+    printf("ctr: %x\n", b);
+    b = inb(VD_STATUS);
+    printf("status: %x\n", b);
+    return 0;
+}
+
+static int vordog_attach(device_t dev) {
+    vd_softc_t sc;
+    struct cdev *_cdev;
+
+    printf("vordog_attach 1\n");
+
+    sc = (vd_softc_t)malloc(sizeof(struct vd_softc), M_TEMP, M_WAITOK);
+    if(sc == NULL)
+	return ENOMEM;
+
+    printf("vordog_attach 2\n");
+    _cdev = make_dev(&vordog_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "vordog0");
+    if(_cdev == NULL) {
+	free(sc, M_TEMP);
+	return EINVAL;
+    }
+
+    sc->_cdev = _cdev;
+    sc->dev = dev;
+
+    printf("vordog_attach 3\n");
+    device_set_softc(dev, sc);
+    CDEV_2_SOFTC(_cdev) = sc;
+
+    sc->open_cnt = 0;
+    sc->init_val = 0;
+
+    return 0;
+}
+
+static int vordog_detach(device_t dev) {
+    vd_softc_t sc;
+    struct cdev *_cdev;
+
+    printf("vordog_detach 1\n");
+    sc = device_get_softc(dev);
+    if(sc->open_cnt != 0)
+	return EBUSY;
+
+    printf("vordog_detach 2\n");
+    _cdev = sc->_cdev;
+
+    printf("vordog_detach 3\n");
+    destroy_dev(_cdev);
+    printf("vordog_detach 4\n");
+
+    free(sc, M_TEMP);
+
+    return 0;
+}
+
+static device_method_t vordog_methods[] = {
+    DEVMETHOD(device_identify, vordog_identify),
+    DEVMETHOD(device_probe, vordog_probe),
+    DEVMETHOD(device_attach, vordog_attach),
+    DEVMETHOD(device_detach, vordog_detach),
+    {0, 0}
+};
+
+static driver_t vordog_driver = {
+    "vordog",
+    vordog_methods,
+    0,
+};
+
+static int
+vordog_evh(module_t mod, int cmd, void *arg) {
+    int r = 0;
+
+    switch(cmd) {
+    case MOD_LOAD:
+	printf("MOD_LOAD\n");
+	break;
+    case MOD_UNLOAD:
+	printf("MOD_UNLOAD\n");
+	vordog_go_detach();	/* remove device from parent */
+	break;
+    case MOD_SHUTDOWN:
+	printf("MOD_SHUTDOWN\n");
+	break;
+    default:
+	r = EINVAL;
+	break;
+    }
+    return r;
+}
+
+static devclass_t vordog_devclass;
+
+DRIVER_MODULE(vordog, nexus, vordog_driver, vordog_devclass,
+	      vordog_evh, NULL);