view vordog.c @ 3:60a234c9c03f

Add 2-clause BSD license
author Thinker K.F. Li <thinker@branda.to>
date Tue, 08 Jul 2008 12:13:58 +0800
parents 0b9854adb86c
children e59963b503cb
line wrap: on
line source

/*-
 * Copyright (c) 2008 Kueifong Li <thinker@branda.to>.
 * All rights reserved.
 *
 * 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.
 */
#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 0
#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

    val = cfg->init_val & 0xff;
    outb(VD_INITVAL, val);
    val = 0x80 | ((cfg->unit & 0x3) << 4) | VD_TIMER_RST;
    outb(VD_CTR, val);
}

static void
reset_timer(int init_val) {
    outb(VD_STATUS, 0xc0);
}

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:
	cfg = (vordog_cfg_t)data;
	sc->init_val = cfg->init_val & 0xff;
	setup_timer(cfg);
	break;
    case VDCTL_RESET:
	reset_timer(sc->init_val);
	break;
    case VDCTL_DISABLE:
	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)
	return;
    vordog = BUS_ADD_CHILD(parent, 10, "vordog", 0);
}

static int vordog_probe(device_t dev) {
    int b;

    b = inb(VD_INITVAL);
    if(b != 0)
	return EINVAL;
    b = inb(VD_CTR);
    if(b != 0)
	return EINVAL;
    b = inb(VD_STATUS);
    if(b != 0)
	return EINVAL;
    return 0;
}

static int vordog_attach(device_t dev) {
    vd_softc_t sc;
    struct cdev *_cdev;

    sc = (vd_softc_t)malloc(sizeof(struct vd_softc), M_TEMP, M_WAITOK);
    if(sc == NULL)
	return ENOMEM;

    _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;

    device_set_softc(dev, sc);
    CDEV_2_SOFTC(_cdev) = sc;

    sc->open_cnt = 0;
    sc->init_val = 0;

    device_set_desc(dev, "Watchdog of Vortex86 SoC");
    device_printf(dev, "<Vortex86 SoC watchdog> at port 0x%x\n", VD_STATUS);

    return 0;
}

static int vordog_detach(device_t dev) {
    vd_softc_t sc;
    struct cdev *_cdev;

    sc = device_get_softc(dev);
    if(sc->open_cnt != 0)
	return EBUSY;

    _cdev = sc->_cdev;

    destroy_dev(_cdev);

    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:
	break;
    case MOD_UNLOAD:
	vordog_go_detach();	/* remove device from parent */
	break;
    case MOD_SHUTDOWN:
	break;
    default:
	r = EINVAL;
	break;
    }
    return r;
}

static devclass_t vordog_devclass;

DRIVER_MODULE(vordog, nexus, vordog_driver, vordog_devclass,
	      vordog_evh, NULL);