/*
* A very simple driver which shows how to use some *very* basic
* features of the Machine Independent VMEbus glue.
*
* Copy this file to sys/dev/vme/foovme.c
*
* You'll also need to add the following lines to the end of the file
* sys/dev/vme/files.vme:
*
* # Dummy VMEbus slave device
* device foo
* attach foo at vme
* file   dev/vme/foovme.c      foo
*
*
* Use with the following entry in your kernel config file:
*
* foo0         at vme0 addr 0xff780000 irq 3 vect 0x80
*/

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>

#include <machine/bus.h>        /* For the bus_space*() macroes */

#include <dev/vme/vmereg.h>     /* For the MI VMEbus glue */
#include <dev/vme/vmevar.h>

/*
* The dummy device maintains state here.
*/
struct foo_softc {
       struct device sc_dev;           /* <-- must exist */
       bus_space_tag_t sc_bust;
       bus_space_handle_t sc_bush;
};

int foomatch __P((struct device *, struct cfdata *, void *));
void fooattach __P((struct device *, struct device *, void *));
int foointr __P((void *));

struct cfattach foo_ca = {
       sizeof(struct foo_softc), foomatch, fooattach
};

extern struct cfdriver foo_cd;

/*
* The VMEbus Address Modifier the dummy device responds to:
*/
#define FOO_AM_MOD      (VME_AM_MBO | VME_AM_A32 | VME_AM_SUPER | VME_AM_DATA)

/*
* Return non-zero if we can access the device at the
* address specified in the config file
*/
int foomatch(parent, cf, aux)
       struct device *parent;
       struct cfdata *cf;
       void *aux;
{
       struct vme_attach_args *va = aux;
       int error;

       error = vme_probe(va->va_vct, va->r[0].offset, 0x4,
           FOO_AM_MOD, VME_D32, NULL, 0);

       return ((error == 0));
}

/*
* Attach the device
*/
void
fooattach(parent, self, aux)
       struct device *parent;
       struct device *self;
       void   *aux;

{
       struct vme_attach_args *va;
       struct foo_softc *sc;
       vme_chipset_tag_t ct;
       vme_mapresc_t resc;
       vme_intr_handle_t ih;
       int i;

       /*
        * The 'vme_attach_args' contains the details from the config
        * file line.
        */
       va = aux;

       /*
        * The 'vme_chipset_tag' identifies the VMEbus we're
        * attaching to.
        */
       ct = va->va_vct;

       /*
        * Here's our soft state structure; allocated automaticaly by
        * the configuration glue.
        */
       sc = (struct foo_softc *)self;

       /*
        * Allocate the dummy device's registers at the offset
        * specified in the config file, of length 4096 bytes,
        * with the VMEbus address modifier defined above.
        */
       if (vme_space_alloc(ct, va->r[0].offset, 0x1000, FOO_AM_MOD))
               panic("foo: vme_space_alloc");

       /*
        * Now get a handle to the registers in sc->sc_bust and sc->sc_bush.
        */
       if (vme_space_map(ct, va->r[0].offset, 0x1000, FOO_AM_MOD, VME_D32, 0,
           &sc->sc_bust, &sc->sc_bush, &resc))
               panic("foo: vme_space_map");

       /*
        * Write a ramp to the device's registers
        */
       for (i = 0; i < 0x1000; i += 4)
               bus_space_write_4(sc->sc_bust, sc->sc_bush, i, i);

       /*
        * The device generates an interrupt, so map it into the system
        */
       if (vme_intr_map(ct, va->ilevel, va->ivector, &ih))
               panic("foo: vme_intr_map");

       /*
        * Finally, arrange for our handler `foointr' to be called
        * whenever the device interrupts. Our soft state pointer
        * will be passed in as the argument to the handler.
        */
       if (vme_intr_establish(ct, ih, IPL_NONE, foointr, sc) == NULL)
               panic("foo: vme_intr_establish");

       printf(": VMEbus test device\n");
}

int
foointr(arg)
       void *arg;
{
       struct foo_softc *sc;

       sc = arg;

       /*
        * Print the contents of the 32-bit location at offet
        * zero of the device.
        */
       printf("foointr: Location 0x0 == 0x%08x\n",
           bus_space_read_4(sc->sc_bust, sc->sc_bush, 0));

       return (1);
}