Mercurial > vordog
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);