/*
 * 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);
}
