code:
/*	$OpenBSD: joy_isa.c,v 1.7 2007/08/01 13:18:18 martin Exp $	*/
/*	$NetBSD: joy.c,v 1.3 1996/05/05 19:46:15 christos Exp $	*/

/*-
 * Copyright (c) 1995 Jean-Marc Zucconi
 * All rights reserved.
 *
 * Ported to NetBSD by Matthieu Herrb <matthieu@laas.fr>
 *
 * 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
 *    in this position and unchanged.
 * 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/errno.h>

#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <machine/joystick.h>
#include <machine/conf.h>

#include <dev/isa/isavar.h>
#include <dev/isa/isareg.h>
#include <dev/ic/i8253reg.h>
#include <i386/isa/joyreg.h>

int		joy_isa_probe(struct device *, void *, void *);
void		joy_isa_attach(struct device *, struct device *, void *);

struct cfattach joy_isa_ca = {
	sizeof(struct joy_softc), joy_isa_probe, joy_isa_attach
};

int
joy_isa_probe(struct device *parent, void *match, void *aux)
{
	struct isa_attach_args *ia = aux;
#ifdef WANT_JOYSTICK_CONNECTED
	int iobase = ia->ia_iobase;

	outb(iobase, 0xff);
	DELAY(10000);		/* 10 ms delay */
	return (inb(iobase) & 0x0f) != 0x0f;
#else
	ia->ia_iosize = JOY_NPORTS;
	ia->ia_msize = 0;
	return 1;
#endif
}

void
joy_isa_attach(struct device *parent, struct device *self, void *aux)
{
	struct joy_softc *sc = (void *) self;
	struct isa_attach_args *ia = aux;
	int iobase = ia->ia_iobase;

	sc->port = iobase;
	sc->timeout[0] = sc->timeout[1] = 0;
	outb(iobase, 0xff);
	DELAY(10000);		/* 10 ms delay */
#if 0
	printf(": joystick%sconnected\n",
	    (inb(iobase) & 0x0f) == 0x0f ? " not " : " ");
#else
	printf("\n");
#endif
}


code:
/*	$OpenBSD: joy_isapnp.c,v 1.7 2007/08/01 13:18:18 martin Exp $	*/
/*	$NetBSD: joy.c,v 1.3 1996/05/05 19:46:15 christos Exp $	*/

/*-
 * Copyright (c) 1995 Jean-Marc Zucconi
 * All rights reserved.
 *
 * Ported to NetBSD by Matthieu Herrb <matthieu@laas.fr>
 *
 * 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
 *    in this position and unchanged.
 * 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/errno.h>

#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <machine/joystick.h>
#include <machine/conf.h>

#include <dev/isa/isavar.h>
#include <dev/isa/isareg.h>
#include <dev/ic/i8253reg.h>
#include <i386/isa/joyreg.h>

int		joy_isapnp_probe(struct device *, void *, void *);
void		joy_isapnp_attach(struct device *, struct device *, void *);

struct cfattach joy_isapnp_ca = {
	sizeof(struct joy_softc), joy_isapnp_probe, joy_isapnp_attach
};

int
joy_isapnp_probe(struct device *parent, void *match, void *aux)
{
	return (1);
}

void
joy_isapnp_attach(struct device *parent, struct device *self, void *aux)
{
	struct joy_softc *sc = (void *) self;
	struct isa_attach_args *ia = aux;
	int iobase = ia->ipa_io[0].base;

	sc->port = iobase;
	sc->timeout[0] = sc->timeout[1] = 0;
	outb(iobase, 0xff);
	DELAY(10000);		/* 10 ms delay */
#if 0
	printf(": joystick%sconnected\n",
	    (inb(iobase) & 0x0f) == 0x0f ? " not " : " ");
#else
	printf("\n");
#endif
}

license: unknown


code:
/*	$OpenBSD: joyreg.h,v 1.5 2007/11/29 10:53:54 deraadt Exp $	*/
/*	$NetBSD: joy.c,v 1.3 1996/05/05 19:46:15 christos Exp $	*/

/*-
 * Copyright (c) 1995 Jean-Marc Zucconi
 * All rights reserved.
 *
 * Ported to NetBSD by Matthieu Herrb <matthieu@laas.fr>
 *
 * 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
 *    in this position and unchanged.
 * 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 * The game port can manage 4 buttons and 4 variable resistors (usually 2
 * joysticks, each with 2 buttons and 2 pots.) via the port at address 0x201.
 * Getting the state of the buttons is done by reading the game port;
 * buttons 1-4 correspond to bits 4-7 and resistors 1-4 (X1, Y1, X2, Y2)
 * to bits 0-3.  If button 1 (resp 2, 3, 4) is pressed, the bit 4 (resp 5,
 * 6, 7) is set to 0 to get the value of a resistor, write the value 0xff
 * at port and wait until the corresponding bit returns to 0.
 */

/*
 * The formulae below only work if u is ``not too large''.
 */
#define USEC2TICKS(u) 	(((u) * 19549) >> 14)
#define TICKS2USEC(u) 	(((u) * 3433) >> 12)

#define JOYPART(d) (minor(d) & 1)
#define JOYUNIT(d) minor(d) >> 1 & 3

#ifndef JOY_TIMEOUT
#define JOY_TIMEOUT   2000	/* 2 milliseconds */
#endif

#define JOY_NPORTS    1

struct joy_softc {
	struct	device sc_dev;
	int	port;
	int	x_off[2], y_off[2];
	int	timeout[2];
};

int		joyopen(dev_t, int, int, struct proc *);
int		joyclose(dev_t, int, int, struct proc *);


code:
/*	$OpenBSD: lms.c,v 1.20 2007/04/10 22:37:17 miod Exp $	*/
/*	$NetBSD: lms.c,v 1.38 2000/01/08 02:57:25 takemura Exp $	*/

/*-
 * Copyright (c) 1993, 1994 Charles M. Hannum.
 * Copyright (c) 1992, 1993 Erik Forsberg.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY ``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 I 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/systm.h>
#include <sys/ioctl.h>
#include <sys/device.h>

#include <machine/bus.h>
#include <machine/intr.h>

#include <dev/isa/isavar.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>

#define	LMS_DATA	0       /* offset for data port, read-only */
#define	LMS_SIGN	1       /* offset for signature port, read-write */
#define	LMS_INTR	2       /* offset for interrupt port, read-only */
#define	LMS_CNTRL	2       /* offset for control port, write-only */
#define	LMS_CONFIG	3	/* for configuration port, read-write */
#define	LMS_NPORTS	4

struct lms_softc {		/* driver status information */
	struct device sc_dev;
	void *sc_ih;

	bus_space_tag_t sc_iot;		/* bus i/o space identifier */
	bus_space_handle_t sc_ioh;	/* bus i/o handle */

	int sc_enabled; /* device is open */
	int oldbuttons;	/* mouse button status */

	struct device *sc_wsmousedev;
};

int lmsprobe(struct device *, void *, void *);
void lmsattach(struct device *, struct device *, void *);
int lmsintr(void *);

struct cfattach lms_ca = {
	sizeof(struct lms_softc), lmsprobe, lmsattach
};

int	lms_enable(void *);
int	lms_ioctl(void *, u_long, caddr_t, int, struct proc *);
void	lms_disable(void *);

const struct wsmouse_accessops lms_accessops = {
	lms_enable,
	lms_ioctl,
	lms_disable,
};

int
lmsprobe(struct device *parent, void *match, void *aux)
{
	struct isa_attach_args *ia = aux;
	bus_space_tag_t iot = ia->ia_iot;
	bus_space_handle_t ioh;
	int rv;

	/* Disallow wildcarded i/o base. */
	if (ia->ia_iobase == IOBASEUNK)
		return 0;

	/* Map the i/o space. */
	if (bus_space_map(iot, ia->ia_iobase, LMS_NPORTS, 0, &ioh))
		return 0;

	rv = 0;

	/* Configure and check for port present. */
	bus_space_write_1(iot, ioh, LMS_CONFIG, 0x91);
	delay(10);
	bus_space_write_1(iot, ioh, LMS_SIGN, 0x0c);
	delay(10);
	if (bus_space_read_1(iot, ioh, LMS_SIGN) != 0x0c)
		goto out;
	bus_space_write_1(iot, ioh, LMS_SIGN, 0x50);
	delay(10);
	if (bus_space_read_1(iot, ioh, LMS_SIGN) != 0x50)
		goto out;

	/* Disable interrupts. */
	bus_space_write_1(iot, ioh, LMS_CNTRL, 0x10);

	rv = 1;
	ia->ia_iosize = LMS_NPORTS;
	ia->ia_msize = 0;

out:
	bus_space_unmap(iot, ioh, LMS_NPORTS);
	return rv;
}

void
lmsattach(struct device *parent, struct device *self, void *aux)
{
	struct lms_softc *sc = (void *)self;
	struct isa_attach_args *ia = aux;
	bus_space_tag_t iot = ia->ia_iot;
	bus_space_handle_t ioh;
	struct wsmousedev_attach_args a;

	printf("\n");

	if (bus_space_map(iot, ia->ia_iobase, LMS_NPORTS, 0, &ioh)) {
		printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
		return;
	}

	/* Other initialization was done by lmsprobe. */
	sc->sc_iot = iot;
	sc->sc_ioh = ioh;
	sc->sc_enabled = 0;

	sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_PULSE,
	    IPL_TTY, lmsintr, sc, sc->sc_dev.dv_xname);

	a.accessops = &lms_accessops;
	a.accesscookie = sc;

	/*
	 * Attach the wsmouse, saving a handle to it.
	 * Note that we don't need to check this pointer against NULL
	 * here or in psmintr, because if this fails lms_enable() will
	 * never be called, so lmsintr() will never be called.
	 */
	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
}

int
lms_enable(void *v)
{
	struct lms_softc *sc = v;

	if (sc->sc_enabled)
		return EBUSY;

	sc->sc_enabled = 1;
	sc->oldbuttons = 0;

	/* Enable interrupts. */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMS_CNTRL, 0);

	return 0;
}

void
lms_disable(void *v)
{
	struct lms_softc *sc = v;

	/* Disable interrupts. */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMS_CNTRL, 0x10);

	sc->sc_enabled = 0;
}

int
lms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
#if 0
	struct lms_softc *sc = v;
#endif

	switch (cmd) {
	case WSMOUSEIO_GTYPE:
		*(u_int *)data = WSMOUSE_TYPE_LMS;
		return (0);
	}
	return (-1);
}

int
lmsintr(void *arg)
{
	struct lms_softc *sc = arg;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	u_char hi, lo;
	signed char dx, dy;
	u_int buttons;
	int changed;

	if (!sc->sc_enabled)
		/* Interrupts are not expected. */
		return 0;

	bus_space_write_1(iot, ioh, LMS_CNTRL, 0xab);
	hi = bus_space_read_1(iot, ioh, LMS_DATA);
	bus_space_write_1(iot, ioh, LMS_CNTRL, 0x90);
	lo = bus_space_read_1(iot, ioh, LMS_DATA);
	dx = ((hi & 0x0f) << 4) | (lo & 0x0f);
	/* Bounding at -127 avoids a bug in XFree86. */
	dx = (dx == -128) ? -127 : dx;

	bus_space_write_1(iot, ioh, LMS_CNTRL, 0xf0);
	hi = bus_space_read_1(iot, ioh, LMS_DATA);
	bus_space_write_1(iot, ioh, LMS_CNTRL, 0xd0);
	lo = bus_space_read_1(iot, ioh, LMS_DATA);
	dy = ((hi & 0x0f) << 4) | (lo & 0x0f);
	dy = (dy == -128) ? 127 : -dy;

	bus_space_write_1(iot, ioh, LMS_CNTRL, 0);

	buttons = ((hi & 0x80) ? 0 : 0x1) |
		((hi & 0x40) ? 0 : 0x2) |
		((hi & 0x20) ? 0 : 0x4);
	changed = (buttons ^ sc->oldbuttons);
	sc->oldbuttons = buttons;

	if (dx || dy || changed)
		wsmouse_input(sc->sc_wsmousedev,
			      buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);

	return -1;
}

struct cfdriver lms_cd = {
	NULL, "lms", DV_DULL
};


code:
/* $OpenBSD: mms.c,v 1.19 2007/04/10 22:37:17 miod Exp $ */
/*	$NetBSD: mms.c,v 1.35 2000/01/08 02:57:25 takemura Exp $	*/

/*-
 * Copyright (c) 1993, 1994 Charles M. Hannum.
 * Copyright (c) 1992, 1993 Erik Forsberg.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY ``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 I 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/systm.h>
#include <sys/ioctl.h>
#include <sys/device.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <dev/isa/isavar.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>

#define	MMS_ADDR	0	/* offset for register select */
#define	MMS_DATA	1	/* offset for InPort data */
#define	MMS_IDENT	2	/* offset for identification register */
#define	MMS_NPORTS	4

struct mms_softc {		/* driver status information */
	struct device sc_dev;
	void *sc_ih;

	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_ioh;

	int sc_enabled; /* device is open */

	struct device *sc_wsmousedev;
};

int mmsprobe(struct device *, void *, void *);
void mmsattach(struct device *, struct device *, void *);
int mmsintr(void *);

struct cfattach mms_ca = {
	sizeof(struct mms_softc), mmsprobe, mmsattach
};

int	mms_enable(void *);
int	mms_ioctl(void *, u_long, caddr_t, int, struct proc *);
void	mms_disable(void *);

const struct wsmouse_accessops mms_accessops = {
	mms_enable,
	mms_ioctl,
	mms_disable,
};

int
mmsprobe(struct device *parent, void *match, void *aux)
{
	struct isa_attach_args *ia = aux;
	bus_space_tag_t iot = ia->ia_iot;
	bus_space_handle_t ioh;
	int rv;

	/* Disallow wildcarded i/o address. */
	if (ia->ia_iobase == IOBASEUNK)
		return 0;

	/* Map the i/o space. */
	if (bus_space_map(iot, ia->ia_iobase, MMS_NPORTS, 0, &ioh))
		return 0;

	rv = 0;

	/* Read identification register to see if present */
	if (bus_space_read_1(iot, ioh, MMS_IDENT) != 0xde)
		goto out;

	/* Seems it was there; reset. */
	bus_space_write_1(iot, ioh, MMS_ADDR, 0x87);

	rv = 1;
	ia->ia_iosize = MMS_NPORTS;
	ia->ia_msize = 0;

out:
	bus_space_unmap(iot, ioh, MMS_NPORTS);
	return rv;
}

void
mmsattach(struct device *parent, struct device *self, void *aux)
{
	struct mms_softc *sc = (void *)self;
	struct isa_attach_args *ia = aux;
	bus_space_tag_t iot = ia->ia_iot;
	bus_space_handle_t ioh;
	struct wsmousedev_attach_args a;

	printf("\n");

	if (bus_space_map(iot, ia->ia_iobase, MMS_NPORTS, 0, &ioh)) {
		printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
		return;
	}

	/* Other initialization was done by mmsprobe. */
	sc->sc_iot = iot;
	sc->sc_ioh = ioh;
	sc->sc_enabled = 0;

	sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_PULSE,
	    IPL_TTY, mmsintr, sc, sc->sc_dev.dv_xname);

	a.accessops = &mms_accessops;
	a.accesscookie = sc;

	/*
	 * Attach the wsmouse, saving a handle to it.
	 * Note that we don't need to check this pointer against NULL
	 * here or in psmintr, because if this fails lms_enable() will
	 * never be called, so lmsintr() will never be called.
	 */
	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
}

int
mms_enable(void *v)
{
	struct mms_softc *sc = v;

	if (sc->sc_enabled)
		return EBUSY;

	sc->sc_enabled = 1;

	/* Enable interrupts. */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_ADDR, 0x07);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_DATA, 0x09);

	return 0;
}

void
mms_disable(void *v)
{
	struct mms_softc *sc = v;

	/* Disable interrupts. */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_ADDR, 0x87);

	sc->sc_enabled = 0;
}

int
mms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
#if 0
	struct mms_softc *sc = v;
#endif

	switch (cmd) {
	case WSMOUSEIO_GTYPE:
		*(u_int *)data = WSMOUSE_TYPE_MMS;
		return (0);
	}
	return (-1);
}

int
mmsintr(void *arg)
{
	struct mms_softc *sc = arg;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	u_char status;
	signed char dx, dy;
	u_int buttons;
	int changed;

	if (!sc->sc_enabled)
		/* Interrupts are not expected. */
		return 0;

	/* Freeze InPort registers (disabling interrupts). */
	bus_space_write_1(iot, ioh, MMS_ADDR, 0x07);
	bus_space_write_1(iot, ioh, MMS_DATA, 0x29);

	bus_space_write_1(iot, ioh, MMS_ADDR, 0x00);
	status = bus_space_read_1(iot, ioh, MMS_DATA);

	if (status & 0x40) {
		bus_space_write_1(iot, ioh, MMS_ADDR, 1);
		dx = bus_space_read_1(iot, ioh, MMS_DATA);
		/* Bounding at -127 avoids a bug in XFree86. */
		dx = (dx == -128) ? -127 : dx;

		bus_space_write_1(iot, ioh, MMS_ADDR, 2);
		dy = bus_space_read_1(iot, ioh, MMS_DATA);
		dy = (dy == -128) ? 127 : -dy;
	} else
		dx = dy = 0;

	/* Unfreeze InPort registers (reenabling interrupts). */
	bus_space_write_1(iot, ioh, MMS_ADDR, 0x07);
	bus_space_write_1(iot, ioh, MMS_DATA, 0x09);

	buttons = ((status & 0x04) ? 0x1 : 0) |
		((status & 0x02) ? 0x2 : 0) |
		((status & 0x01) ? 0x4 : 0);
	changed = status & 0x38;

	if (dx || dy || changed)
		wsmouse_input(sc->sc_wsmousedev,
			      buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);

	return -1;
}

struct cfdriver mms_cd = {
	NULL, "mms", DV_DULL
};


code:
/*	$OpenBSD: npx.c,v 1.61 2015/02/11 05:54:48 dlg Exp $	*/
/*	$NetBSD: npx.c,v 1.57 1996/05/12 23:12:24 mycroft Exp $	*/

#if 0
#define IPRINTF(x)	printf x
#else
#define	IPRINTF(x)
#endif

/*-
 * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
 * Copyright (c) 1990 William Jolitz.
 * Copyright (c) 1991 The Regents of the University of California.
 * 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.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 *
 *	@(#)npx.c	7.2 (Berkeley) 5/12/91
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/ioctl.h>
#include <sys/device.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/npx.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <machine/pcb.h>
#include <machine/trap.h>
#include <machine/specialreg.h>
#include <machine/i8259.h>
#include <machine/lock.h>

#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>

/*
 * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
 *
 * We do lazy initialization and switching using the TS bit in cr0 and the
 * MDP_USEDFPU bit in mdproc.
 *
 * DNA exceptions are handled like this:
 *
 * 1) If there is no NPX, return and go to the emulator.
 * 2) If someone else has used the NPX, save its state into that process's PCB.
 * 3a) If MDP_USEDFPU is not set, set it and initialize the NPX.
 * 3b) Otherwise, reload the process's previous NPX state.
 *
 * When a process is created or exec()s, its saved cr0 image has the TS bit
 * set and the MDP_USEDFPU bit clear.  The MDP_USEDFPU bit is set when the
 * process first gets a DNA and the NPX is initialized.  The TS bit is turned
 * off when the NPX is used, and turned on again later when the process's NPX
 * state is saved.
 */

#define	fldcw(addr)		__asm("fldcw %0" : : "m" (*addr))
#define	fnclex()		__asm("fnclex")
#define	fninit()		__asm("fninit")
#define	fnsave(addr)		__asm("fnsave %0" : "=m" (*addr))
#define	fnstcw(addr)		__asm("fnstcw %0" : "=m" (*addr))
#define	fnstsw(addr)		__asm("fnstsw %0" : "=m" (*addr))
#define	fp_divide_by_0()	__asm("fldz; fld1; fdiv %st,%st(1); fwait")
#define	frstor(addr)		__asm("frstor %0" : : "m" (*addr))
#define	fwait()			__asm("fwait")
#define	clts()			__asm("clts")
#define	stts()			lcr0(rcr0() | CR0_TS)

/*
 * The mxcsr_mask for this host, taken from fxsave() on the primary CPU
 */
uint32_t	fpu_mxcsr_mask;

int npxintr(void *);
static int npxprobe1(struct isa_attach_args *);
static int x86fpflags_to_siginfo(u_int32_t);

struct npx_softc {
	struct device sc_dev;
	void *sc_ih;
};

int npxprobe(struct device *, void *, void *);
void npxattach(struct device *, struct device *, void *);

struct cfattach npx_ca = {
	sizeof(struct npx_softc), npxprobe, npxattach
};

struct cfdriver npx_cd = {
	NULL, "npx", DV_DULL
};

enum npx_type {
	NPX_NONE = 0,
	NPX_INTERRUPT,
	NPX_EXCEPTION,
	NPX_BROKEN,
	NPX_CPUID,
};

static	enum npx_type		npx_type;
static	volatile u_int		npx_intrs_while_probing;
static	volatile u_int		npx_traps_while_probing;

extern int i386_fpu_present;
extern int i386_fpu_exception;
extern int i386_fpu_fdivbug;

#define fxsave(addr)		__asm("fxsave %0" : "=m" (*addr))
#define fxrstor(addr)		__asm("fxrstor %0" : : "m" (*addr))
#define ldmxcsr(addr)		__asm("ldmxcsr %0" : : "m" (*addr))

static __inline void
fpu_save(union savefpu *addr)
{

	if (i386_use_fxsave) {
		fxsave(&addr->sv_xmm);
		/* FXSAVE doesn't FNINIT like FNSAVE does -- so do it here. */
		fninit();
	} else
		fnsave(&addr->sv_87);
}

static int
npxdna_notset(struct cpu_info *ci)
{
	panic("npxdna vector not initialized");
}

int    (*npxdna_func)(struct cpu_info *) = npxdna_notset;
int    npxdna_s87(struct cpu_info *);
int    npxdna_xmm(struct cpu_info *);
void   npxexit(void);

/*
 * Special interrupt handlers.  Someday intr0-intr15 will be used to count
 * interrupts.  We'll still need a special exception 16 handler.  The busy
 * latch stuff in probintr() can be moved to npxprobe().
 */
void probeintr(void);
asm (".text\n\t"
"probeintr:\n\t"
	"ss\n\t"
	"incl	npx_intrs_while_probing\n\t"
	"pushl	%eax\n\t"
	"movb	$0x20,%al	# EOI (asm in strings loses cpp features)\n\t"
	"outb	%al,$0xa0	# IO_ICU2\n\t"
	"outb	%al,$0x20	# IO_ICU1\n\t"
	"movb	$0,%al\n\t"
	"outb	%al,$0xf0	# clear BUSY# latch\n\t"
	"popl	%eax\n\t"
	"iret\n\t");

void probetrap(void);
asm (".text\n\t"
"probetrap:\n\t"
	"ss\n\t"
	"incl	npx_traps_while_probing\n\t"
	"fnclex\n\t"
	"iret\n\t");

static inline int
npxprobe1(struct isa_attach_args *ia)
{
	int control;
	int status;

	ia->ia_iosize = 16;
	ia->ia_msize = 0;

	/*
	 * Finish resetting the coprocessor, if any.  If there is an error
	 * pending, then we may get a bogus IRQ13, but probeintr() will handle
	 * it OK.  Bogus halts have never been observed, but we enabled
	 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
	 */
	fninit();
	delay(1000);		/* wait for any IRQ13 (fwait might hang) */

	/*
	 * Check for a status of mostly zero.
	 */
	status = 0x5a5a;
	fnstsw(&status);
	if ((status & 0xb8ff) == 0) {
		/*
		 * Good, now check for a proper control word.
		 */
		control = 0x5a5a;
		fnstcw(&control);
		if ((control & 0x1f3f) == 0x033f) {
			/*
			 * We have an npx, now divide by 0 to see if exception
			 * 16 works.
			 */
			control &= ~(1 << 2);	/* enable divide by 0 trap */
			fldcw(&control);
			npx_traps_while_probing = npx_intrs_while_probing = 0;
			fp_divide_by_0();
			delay(1);
			if (npx_traps_while_probing != 0) {
				/*
				 * Good, exception 16 works.
				 */
				npx_type = NPX_EXCEPTION;
				ia->ia_irq = IRQUNK;	/* zap the interrupt */
				i386_fpu_exception = 1;
			} else if (npx_intrs_while_probing != 0) {
				/*
				 * Bad, we are stuck with IRQ13.
				 */
				npx_type = NPX_INTERRUPT;
			} else {
				/*
				 * Worse, even IRQ13 is broken.
				 */
				npx_type = NPX_BROKEN;
				ia->ia_irq = IRQUNK;
			}
			return 1;
		}
	}

	/*
	 * Probe failed.  There is no usable FPU.
	 */
	npx_type = NPX_NONE;
	return 0;
}

/*
 * Probe routine.  Initialize cr0 to give correct behaviour for [f]wait
 * whether the device exists or not (XXX should be elsewhere).  Set flags
 * to tell npxattach() what to do.  Modify device struct if npx doesn't
 * need to use interrupts.  Return 1 if device exists.
 */
int
npxprobe(struct device *parent, void *match, void *aux)
{
	struct	isa_attach_args *ia = aux;
	int	irq;
	int	result;
	u_long	save_eflags;
	unsigned save_imen;
	struct	gate_descriptor save_idt_npxintr;
	struct	gate_descriptor save_idt_npxtrap;

	if (cpu_feature & CPUID_FPU) {
		npx_type = NPX_CPUID;
		i386_fpu_exception = 1;
		ia->ia_irq = IRQUNK;	/* Don't want the interrupt vector */
		ia->ia_iosize = 16;
		ia->ia_msize = 0;
		return 1;
	}

	/*
	 * This routine is now just a wrapper for npxprobe1(), to install
	 * special npx interrupt and trap handlers, to enable npx interrupts
	 * and to disable other interrupts.  Someday isa_configure() will
	 * install suitable handlers and run with interrupts enabled so we
	 * won't need to do so much here.
	 */
	irq = NRSVIDT + ia->ia_irq;
	save_eflags = read_eflags();
	disable_intr();
	save_idt_npxintr = idt[irq];
	save_idt_npxtrap = idt[16];
	setgate(&idt[irq], probeintr, 0, SDT_SYS386IGT, SEL_KPL, GICODE_SEL);
	setgate(&idt[16], probetrap, 0, SDT_SYS386TGT, SEL_KPL, GCODE_SEL);
	save_imen = imen;
	imen = ~((1 << IRQ_SLAVE) | (1 << ia->ia_irq));
	SET_ICUS();

	/*
	 * Partially reset the coprocessor, if any.  Some BIOS's don't reset
	 * it after a warm boot.
	 */
	outb(0xf1, 0);		/* full reset on some systems, NOP on others */
	delay(1000);
	outb(0xf0, 0);		/* clear BUSY# latch */

	/*
	 * We set CR0 in locore to trap all ESC and WAIT instructions.
	 * We have to turn off the CR0_EM bit temporarily while probing.
	 */
	lcr0(rcr0() & ~(CR0_EM|CR0_TS));
	enable_intr();
	result = npxprobe1(ia);
	disable_intr();
	lcr0(rcr0() | (CR0_EM|CR0_TS));

	imen = save_imen;
	SET_ICUS();
	idt[irq] = save_idt_npxintr;
	idt[16] = save_idt_npxtrap;
	write_eflags(save_eflags);
	return (result);
}

int npx586bug1(int, int);
asm (".text\n\t"
"npx586bug1:\n\t"
	"fildl	4(%esp)		# x\n\t"
	"fildl	8(%esp)		# y\n\t"
	"fld	%st(1)\n\t"
	"fdiv	%st(1),%st	# x/y\n\t"
	"fmulp	%st,%st(1)	# (x/y)*y\n\t"
	"fsubrp	%st,%st(1)	# x-(x/y)*y\n\t"
	"pushl	$0\n\t"
	"fistpl	(%esp)\n\t"
	"popl	%eax\n\t"
	"ret\n\t");

void
npxinit(struct cpu_info *ci)
{
	lcr0(rcr0() & ~(CR0_EM|CR0_TS));
	fninit();
	if (npx586bug1(4195835, 3145727) != 0) {
		i386_fpu_fdivbug = 1;
		printf("%s: WARNING: Pentium FDIV bug detected!\n",
		    ci->ci_dev.dv_xname);
	}
	if (fpu_mxcsr_mask == 0 && i386_use_fxsave) {
		struct savexmm xm __attribute__((aligned(16)));

		bzero(&xm, sizeof(xm));
		fxsave(&xm);
		if (xm.sv_env.en_mxcsr_mask)
			fpu_mxcsr_mask = xm.sv_env.en_mxcsr_mask;
		else
			fpu_mxcsr_mask = __INITIAL_MXCSR_MASK__;
	}
	lcr0(rcr0() | (CR0_TS));
}

/*
 * Attach routine - announce which it is, and wire into system
 */
void
npxattach(struct device *parent, struct device *self, void *aux)
{
	struct npx_softc *sc = (void *)self;
	struct isa_attach_args *ia = aux;

	switch (npx_type) {
	case NPX_INTERRUPT:
		printf("\n");
		lcr0(rcr0() & ~CR0_NE);
		sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq,
		    IST_EDGE, IPL_NONE, npxintr, 0, sc->sc_dev.dv_xname);
		break;
	case NPX_EXCEPTION:
		printf(": using exception 16\n");
		break;
	case NPX_CPUID:
		printf(": reported by CPUID; using exception 16\n");
		npx_type = NPX_EXCEPTION;
		break;
	case NPX_BROKEN:
		printf(": error reporting broken; not using\n");
		npx_type = NPX_NONE;
		return;
	case NPX_NONE:
		return;
	}

	npxinit(&cpu_info_primary);
	i386_fpu_present = 1;

	if (i386_use_fxsave)
		npxdna_func = npxdna_xmm;
	else
		npxdna_func = npxdna_s87;
}

/*
 * Record the FPU state and reinitialize it all except for the control word.
 * Then generate a SIGFPE.
 *
 * Reinitializing the state allows naive SIGFPE handlers to longjmp without
 * doing any fixups.
 *
 * XXX there is currently no way to pass the full error state to signal
 * handlers, and if this is a nested interrupt there is no way to pass even
 * a status code!  So there is no way to have a non-naive SIGFPE handler.  At
 * best a handler could do an fninit followed by an fldcw of a static value.
 * fnclex would be of little use because it would leave junk on the FPU stack.
 * Returning from the handler would be even less safe than usual because
 * IRQ13 exception handling makes exceptions even less precise than usual.
 */
int
npxintr(void *arg)
{
	struct cpu_info *ci = curcpu();
	struct proc *p = ci->ci_fpcurproc;
	union savefpu *addr;
	struct intrframe *frame = arg;
	int code;
	union sigval sv;

	uvmexp.traps++;
	IPRINTF(("%s: fp intr\n", ci->ci_dev.dv_xname));

	if (p == NULL || npx_type == NPX_NONE) {
		/* XXX no %p in stand/printf.c.  Cast to quiet gcc -Wall. */
		printf("npxintr: p = %lx, curproc = %lx, npx_type = %d\n",
		       (u_long) p, (u_long) curproc, npx_type);
		panic("npxintr from nowhere");
	}
	/*
	 * Clear the interrupt latch.
	 */
	outb(0xf0, 0);
	/*
	 * If we're saving, ignore the interrupt.  The FPU will happily
	 * generate another one when we restore the state later.
	 */
	if (ci->ci_fpsaving)
		return (1);

#ifdef DIAGNOSTIC
	/*
	 * At this point, fpcurproc should be curproc.  If it wasn't, the TS
	 * bit should be set, and we should have gotten a DNA exception.
	 */
	if (p != curproc)
		panic("npxintr: wrong process");
#endif

	/*
	 * Find the address of fpcurproc's saved FPU state.  (Given the
	 * invariant above, this is always the one in curpcb.)
	 */
	addr = &p->p_addr->u_pcb.pcb_savefpu;
	/*
	 * Save state.  This does an implied fninit.  It had better not halt
	 * the cpu or we'll hang.
	 */
	fpu_save(addr);
	fwait();
	/*
	 * Restore control word (was clobbered by fpu_save).
	 */
	if (i386_use_fxsave) {
		fldcw(&addr->sv_xmm.sv_env.en_cw);
		/*
		 * FNINIT doesn't affect MXCSR or the XMM registers;
		 * no need to re-load MXCSR here.
		 */
	} else
		fldcw(&addr->sv_87.sv_env.en_cw);
	fwait();
	/*
	 * Remember the exception status word and tag word.  The current
	 * (almost fninit'ed) fpu state is in the fpu and the exception
	 * state just saved will soon be junk.  However, the implied fninit
	 * doesn't change the error pointers or register contents, and we
	 * preserved the control word and will copy the status and tag
	 * words, so the complete exception state can be recovered.
	 */
	if (i386_use_fxsave) {
	        addr->sv_xmm.sv_ex_sw = addr->sv_xmm.sv_env.en_sw;
	        addr->sv_xmm.sv_ex_tw = addr->sv_xmm.sv_env.en_tw;
	} else {
	        addr->sv_87.sv_ex_sw = addr->sv_87.sv_env.en_sw;
	        addr->sv_87.sv_ex_tw = addr->sv_87.sv_env.en_tw;
	}

	/*
	 * Pass exception to process.  If it's the current process, try to do
	 * it immediately.
	 */
	if (p == curproc && USERMODE(frame->if_cs, frame->if_eflags)) {
		/*
		 * Interrupt is essentially a trap, so we can afford to call
		 * the SIGFPE handler (if any) as soon as the interrupt
		 * returns.
		 *
		 * XXX little or nothing is gained from this, and plenty is
		 * lost - the interrupt frame has to contain the trap frame
		 * (this is otherwise only necessary for the rescheduling trap
		 * in doreti, and the frame for that could easily be set up
		 * just before it is used).
		 */
		p->p_md.md_regs = (struct trapframe *)&frame->if_fs;

		/*
		 * Encode the appropriate code for detailed information on
		 * this exception.
		 */
		if (i386_use_fxsave)
			code = x86fpflags_to_siginfo(addr->sv_xmm.sv_ex_sw);
		else
			code = x86fpflags_to_siginfo(addr->sv_87.sv_ex_sw);
		sv.sival_int = frame->if_eip;
		trapsignal(p, SIGFPE, T_ARITHTRAP, code, sv);
	} else {
		/*
		 * Nested interrupt.  These losers occur when:
		 *	o an IRQ13 is bogusly generated at a bogus time, e.g.:
		 *		o immediately after an fnsave or frstor of an
		 *		  error state.
		 *		o a couple of 386 instructions after
		 *		  "fstpl _memvar" causes a stack overflow.
		 *	  These are especially nasty when combined with a
		 *	  trace trap.
		 *	o an IRQ13 occurs at the same time as another higher-
		 *	  priority interrupt.
		 *
		 * Treat them like a true async interrupt.
		 */
		psignal(p, SIGFPE);
	}

	return (1);
}

void
npxtrap(struct trapframe *frame)
{
	struct proc *p = curcpu()->ci_fpcurproc;
	union savefpu *addr = &p->p_addr->u_pcb.pcb_savefpu;
	u_int32_t mxcsr, statbits;
	int code;
	union sigval sv;

#ifdef DIAGNOSTIC
	/*
	 * At this point, fpcurproc should be curproc.  If it wasn't, the TS
	 * bit should be set, and we should have gotten a DNA exception.
	 */
	if (p != curproc)
		panic("npxtrap: wrong process");
#endif

	fxsave(&addr->sv_xmm);
	mxcsr = addr->sv_xmm.sv_env.en_mxcsr;
	statbits = mxcsr;
	mxcsr &= ~0x3f;
	ldmxcsr(&mxcsr);
	addr->sv_xmm.sv_ex_sw = addr->sv_xmm.sv_env.en_sw;
	addr->sv_xmm.sv_ex_tw = addr->sv_xmm.sv_env.en_tw;
	code = x86fpflags_to_siginfo (statbits);
	sv.sival_int = frame->tf_eip;
	KERNEL_LOCK();
	trapsignal(p, SIGFPE, frame->tf_err, code, sv);
	KERNEL_UNLOCK();
}

static int
x86fpflags_to_siginfo(u_int32_t flags)
{
        int i;
        static int x86fp_siginfo_table[] = {
                FPE_FLTINV, /* bit 0 - invalid operation */
                FPE_FLTRES, /* bit 1 - denormal operand */
                FPE_FLTDIV, /* bit 2 - divide by zero   */
                FPE_FLTOVF, /* bit 3 - fp overflow      */
                FPE_FLTUND, /* bit 4 - fp underflow     */
                FPE_FLTRES, /* bit 5 - fp precision     */
                FPE_FLTINV, /* bit 6 - stack fault      */
        };

        for (i=0;i < sizeof(x86fp_siginfo_table)/sizeof(int); i++) {
                if (flags & (1 << i))
                        return (x86fp_siginfo_table[i]);
        }
        /* punt if flags not set */
        return (FPE_FLTINV);
}

/*
 * Implement device not available (DNA) exception
 *
 * If we were the last process to use the FPU, we can simply return.
 * Otherwise, we save the previous state, if necessary, and restore our last
 * saved state.
 */
int
npxdna_xmm(struct cpu_info *ci)
{
	union savefpu *sfp;
	struct proc *p;
	int s;

	if (ci->ci_fpsaving) {
		printf("recursive npx trap; cr0=%x\n", rcr0());
		return (0);
	}

	s = splipi();		/* lock out IPI's while we clean house.. */

#ifdef MULTIPROCESSOR
	p = ci->ci_curproc;
#else
	p = curproc;
#endif

	IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p,
	    (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : ""));

	/*
	 * XXX should have a fast-path here when no save/restore is necessary
	 */
	/*
	 * Initialize the FPU state to clear any exceptions.  If someone else
	 * was using the FPU, save their state (which does an implicit
	 * initialization).
	 */
	if (ci->ci_fpcurproc != NULL) {
		IPRINTF(("%s: fp save %lx\n", ci->ci_dev.dv_xname,
		    (u_long)ci->ci_fpcurproc));
		npxsave_cpu(ci, ci->ci_fpcurproc != &proc0);
	} else {
		clts();
		IPRINTF(("%s: fp init\n", ci->ci_dev.dv_xname));
		fninit();
		fwait();
		stts();
	}
	splx(s);

	IPRINTF(("%s: done saving\n", ci->ci_dev.dv_xname));
	KDASSERT(ci->ci_fpcurproc == NULL);
#ifndef MULTIPROCESSOR
	KDASSERT(p->p_addr->u_pcb.pcb_fpcpu == NULL);
#else
	if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
		npxsave_proc(p, 1);
#endif
	p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
	clts();
	s = splipi();
	ci->ci_fpcurproc = p;
	p->p_addr->u_pcb.pcb_fpcpu = ci;
	splx(s);
	uvmexp.fpswtch++;

	sfp = &p->p_addr->u_pcb.pcb_savefpu;

	if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
		bzero(&sfp->sv_xmm, sizeof(sfp->sv_xmm));
		sfp->sv_xmm.sv_env.en_cw = __INITIAL_NPXCW__;
		sfp->sv_xmm.sv_env.en_mxcsr = __INITIAL_MXCSR__;
		fxrstor(&sfp->sv_xmm);
		p->p_md.md_flags |= MDP_USEDFPU;
	} else {
		static double	zero = 0.0;

		/*
		 * amd fpu does not restore fip, fdp, fop on fxrstor
		 * thus leaking other process's execution history.
		 */
		fnclex();
		__asm volatile("ffree %%st(7)\n\tfldl %0" : : "m" (zero));
		fxrstor(&sfp->sv_xmm);
	}

	return (1);
}

int
npxdna_s87(struct cpu_info *ci)
{
	union savefpu *sfp;
	struct proc *p;
	int s;

	KDASSERT(i386_use_fxsave == 0);

	if (ci->ci_fpsaving) {
		printf("recursive npx trap; cr0=%x\n", rcr0());
		return (0);
	}

	s = splipi();		/* lock out IPI's while we clean house.. */
#ifdef MULTIPROCESSOR
	p = ci->ci_curproc;
#else
	p = curproc;
#endif

	IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p,
	    (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : ""));

	/*
	 * If someone else was using our FPU, save their state (which does an
	 * implicit initialization); otherwise, initialize the FPU state to
	 * clear any exceptions.
	 */
	if (ci->ci_fpcurproc != NULL) {
		IPRINTF(("%s: fp save %lx\n", ci->ci_dev.dv_xname,
		    (u_long)ci->ci_fpcurproc));
		npxsave_cpu(ci, ci->ci_fpcurproc != &proc0);
	} else {
		clts();
		IPRINTF(("%s: fp init\n", ci->ci_dev.dv_xname));
		fninit();
		fwait();
		stts();
	}
	splx(s);

	IPRINTF(("%s: done saving\n", ci->ci_dev.dv_xname));
	KDASSERT(ci->ci_fpcurproc == NULL);
#ifndef MULTIPROCESSOR
	KDASSERT(p->p_addr->u_pcb.pcb_fpcpu == NULL);
#else
	if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
		npxsave_proc(p, 1);
#endif
	p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
	clts();
	s = splipi();
	ci->ci_fpcurproc = p;
	p->p_addr->u_pcb.pcb_fpcpu = ci;
	splx(s);
	uvmexp.fpswtch++;

	sfp = &p->p_addr->u_pcb.pcb_savefpu;

	if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
		bzero(&sfp->sv_87, sizeof(sfp->sv_87));
		sfp->sv_87.sv_env.en_cw = __INITIAL_NPXCW__;
		sfp->sv_87.sv_env.en_tw = 0xffff;
		frstor(&sfp->sv_87);
		p->p_md.md_flags |= MDP_USEDFPU;
	} else {
		/*
		 * The following frstor may cause an IRQ13 when the state being
		 * restored has a pending error.  The error will appear to have
		 * been triggered by the current (npx) user instruction even
		 * when that instruction is a no-wait instruction that should
		 * not trigger an error (e.g., fnclex).  On at least one 486
		 * system all of the no-wait instructions are broken the same
		 * as frstor, so our treatment does not amplify the breakage.
		 * On at least one 386/Cyrix 387 system, fnclex works correctly
		 * while frstor and fnsave are broken, so our treatment breaks
		 * fnclex if it is the first FPU instruction after a context
		 * switch.
		 */
		frstor(&sfp->sv_87);
	}

	return (1);
}

/*
 * The FNSAVE instruction clears the FPU state.  Rather than reloading the FPU
 * immediately, we clear fpcurproc and turn on CR0_TS to force a DNA and a
 * reload of the FPU state the next time we try to use it.  This routine
 * is only called when forking, core dumping, or debugging, or swapping,
 * so the lazy reload at worst forces us to trap once per fork(), and at best
 * saves us a reload once per fork().
 */
void
npxsave_cpu(struct cpu_info *ci, int save)
{
	struct proc *p;
	int s;

	KDASSERT(ci == curcpu());

	p = ci->ci_fpcurproc;
	if (p == NULL)
		return;

	IPRINTF(("%s: fp cpu %s %lx\n", ci->ci_dev.dv_xname,
	    save ? "save" : "flush", (u_long)p));

	if (save) {
#ifdef DIAGNOSTIC
		if (ci->ci_fpsaving != 0)
			panic("npxsave_cpu: recursive save!");
#endif
		 /*
		  * Set ci->ci_fpsaving, so that any pending exception will be
		  * thrown away.  (It will be caught again if/when the FPU
		  * state is restored.)
		  *
		  * XXX on i386 and earlier, this routine should always be
		  * called at spl0; if it might called with the NPX interrupt
		  * masked, it would be necessary to forcibly unmask the NPX
		  * interrupt so that it could succeed.
		  * XXX this is irrelevant on 486 and above (systems
		  * which report FP failures via traps rather than irq13).
		  * XXX punting for now..
		  */
		clts();
		ci->ci_fpsaving = 1;
		fpu_save(&p->p_addr->u_pcb.pcb_savefpu);
		ci->ci_fpsaving = 0;
		/* It is unclear if this is needed. */
		fwait();
	}

	/*
	 * We set the TS bit in the saved CR0 for this process, so that it
	 * will get a DNA exception on any FPU instruction and force a reload.
	 */
	stts();
	p->p_addr->u_pcb.pcb_cr0 |= CR0_TS;

	s = splipi();
	p->p_addr->u_pcb.pcb_fpcpu = NULL;
	ci->ci_fpcurproc = NULL;
	splx(s);
}

/*
 * Save p's FPU state, which may be on this processor or another processor.
 */
void
npxsave_proc(struct proc *p, int save)
{
	struct cpu_info *ci = curcpu();
	struct cpu_info *oci;

	KDASSERT(p->p_addr != NULL);

	oci = p->p_addr->u_pcb.pcb_fpcpu;
	if (oci == NULL)
		return;

	IPRINTF(("%s: fp proc %s %lx\n", ci->ci_dev.dv_xname,
	    save ? "save" : "flush", (u_long)p));

#if defined(MULTIPROCESSOR)
	if (oci == ci) {
		int s = splipi();
		npxsave_cpu(ci, save);
		splx(s);
	} else {
		IPRINTF(("%s: fp ipi to %s %s %lx\n", ci->ci_dev.dv_xname,
		    oci->ci_dev.dv_xname, save ? "save" : "flush", (u_long)p));

		oci->ci_fpsaveproc = p;
		i386_send_ipi(oci,
		    save ? I386_IPI_SYNCH_FPU : I386_IPI_FLUSH_FPU);
		while (p->p_addr->u_pcb.pcb_fpcpu != NULL)
			SPINLOCK_SPIN_HOOK;
	}
#else
	KASSERT(ci->ci_fpcurproc == p);
	npxsave_cpu(ci, save);
#endif
}

void
fpu_kernel_enter(void)
{
	struct cpu_info	*ci = curcpu();
	uint32_t	 cw;
	int		 s;

	/*
	 * Fast path.  If the kernel was using the FPU before, there
	 * is no work to do besides clearing TS.
	 */
	if (ci->ci_fpcurproc == &proc0) {
		clts();
		return;
	}

	s = splipi();

	if (ci->ci_fpcurproc != NULL) {
		npxsave_cpu(ci, 1);
		uvmexp.fpswtch++;
	}

	/* Claim the FPU */
	ci->ci_fpcurproc = &proc0;

	splx(s);

	/* Disable DNA exceptions */
	clts();

	/* Initialize the FPU */
	fninit();
	cw = __INITIAL_NPXCW__;
	fldcw(&cw);
	if (i386_has_sse || i386_has_sse2) {
		cw = __INITIAL_MXCSR__;
		ldmxcsr(&cw);
	}
}

void
fpu_kernel_exit(void)
{
	/* Enable DNA exceptions */
	stts();
}


code:
/*	$OpenBSD: nvram.h,v 1.7 2004/06/09 10:17:10 art Exp $	*/
/*	$NetBSD: nvram.h,v 1.5 1995/05/05 22:08:43 mycroft Exp $	*/

/*-
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * 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.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 *
 *	@(#)rtc.h	8.1 (Berkeley) 6/11/93
 */

/*
 * The following information is found in the non-volatile RAM in the
 * MC146818A (or DS1287A or other compatible) RTC on AT-compatible PCs.
 */

/* NVRAM byte 0: bios diagnostic */
#define NVRAM_DIAG	(MC_NVRAM_START + 0)	/* RTC offset 0xe */

#define NVRAM_DIAG_BITS		"\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time"

/* NVRAM byte 1: reset code */
#define NVRAM_RESET	(MC_NVRAM_START + 1)	/* RTC offset 0xf */

#define	NVRAM_RESET_RST		0x00		/* normal reset */
#define	NVRAM_RESET_LOAD 	0x04		/* load system */
#define	NVRAM_RESET_JUMP	0x0a		/* jump through 40:67 */

/* NVRAM byte 2: diskette drive type in upper/lower nibble */
#define NVRAM_DISKETTE	(MC_NVRAM_START + 2)	/* RTC offset 0x10 */

#define	NVRAM_DISKETTE_NONE	 0		/* none present */
#define	NVRAM_DISKETTE_360K	 0x10		/* 360K */
#define	NVRAM_DISKETTE_12M	 0x20		/* 1.2M */
#define	NVRAM_DISKETTE_720K	 0x30		/* 720K */
#define	NVRAM_DISKETTE_144M	 0x40		/* 1.44M */
#define	NVRAM_DISKETTE_TYPE5	 0x50		/* 2.88M, presumably */
#define	NVRAM_DISKETTE_TYPE6	 0x60		/* 2.88M */

/* NVRAM byte 6: equipment type */
#define	NVRAM_EQUIPMENT	(MC_NVRAM_START + 6)

#define	NVRAM_EQUIPMENT_FLOPPY	0x01		/* floppy installed */
#define	NVRAM_EQUIPMENT_FPU	0x02		/* FPU installed */
#define	NVRAM_EQUIPMENT_KBD	0x04		/* keyboard installed */
#define	NVRAM_EQUIPMENT_DISPLAY	0x08		/* display installed */
#define	NVRAM_EQUIPMENT_EGAVGA	0x00		/* EGA or VGA */
#define	NVRAM_EQUIPMENT_COLOR40	0x10		/* 40 column color */
#define	NVRAM_EQUIPMENT_COLOR80	0x20		/* 80 column color */
#define	NVRAM_EQUIPMENT_MONO80	0x30		/* 80 column mono */
#define	NVRAM_EQUIPMENT_MONITOR	0x30		/* mask for monitor type */
#define	MVRAM_EQUIPMENT_NFDS	0xC0		/* mask for # of floppies */

/* NVRAM bytes 7 & 8: base memory size */
#define NVRAM_BASELO	(MC_NVRAM_START + 7)	/* low byte; RTC off. 0x15 */
#define NVRAM_BASEHI	(MC_NVRAM_START + 8)	/* high byte; RTC off. 0x16 */

/* NVRAM bytes 9 & 10: extended memory size */
#define NVRAM_EXTLO	(MC_NVRAM_START + 9)	/* low byte; RTC off. 0x17 */
#define NVRAM_EXTHI	(MC_NVRAM_START + 10)	/* high byte; RTC off. 0x18 */

/* NVRAM bytes 34 and 35: extended memory POSTed size */
#define NVRAM_PEXTLO	(MC_NVRAM_START + 34)	/* low byte; RTC off. 0x30 */
#define NVRAM_PEXTHI	(MC_NVRAM_START + 35)	/* high byte; RTC off. 0x31 */

/* NVRAM byte 36: current century.  (please increment in Dec99!) */
#define NVRAM_CENTURY	(MC_NVRAM_START + 36)	/* RTC offset 0x32 */


code:
/*	$OpenBSD: agp_machdep.c,v 1.20 2014/11/16 12:30:57 deraadt Exp $	*/

/*
 * Copyright (c) 2008 - 2009 Owain G. Ainsworth <oga@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2002 Michael Shalayeff
 * 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 AUTHOR ``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 AUTHOR OR HIS RELATIVES 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 MIND, 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/systm.h>
#include <sys/malloc.h>
#include <sys/rwlock.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/agpvar.h>

#include <uvm/uvm_extern.h>

#include <machine/cpufunc.h>
#include <machine/bus.h>
#include <machine/pmap.h>

void
agp_flush_cache(void)
{
	wbinvd();
}

void
agp_flush_cache_range(vaddr_t va, vsize_t sz)
{
	pmap_flush_cache(va, sz);
}

struct agp_map {
	bus_space_tag_t	bst;
	bus_addr_t	addr;
	bus_size_t	size;
	int		flags;
	vaddr_t		va;
};

extern struct extent	*iomem_ex;

int
agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size,
    int flags, struct agp_map **mapp)
{
	struct agp_map *map;
	int error;

	KASSERT(tag == I386_BUS_SPACE_MEM);

	/*
	 * We grab the extent out of the bus region ourselves
	 * so we don't need to do these allocations every time.
	 */
	error = extent_alloc_region(iomem_ex, address, size,
	    EX_NOWAIT | EX_MALLOCOK);
	if (error)
		return (error);

	map = malloc(sizeof(*map), M_AGP, M_WAITOK | M_CANFAIL);
	if (map == NULL)
		return (ENOMEM);

	map->bst = tag;
	map->addr = address;
	map->size = size;
	map->flags = flags;

	map->va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none, &kd_waitok);
	if (map->va == 0) {
		free(map, M_AGP, sizeof(*map));
		return (ENOMEM);
	}

	*mapp = map;
	return (0);
}

void
agp_destroy_map(struct agp_map *map)
{
	if (extent_free(iomem_ex, map->addr, map->size,
	    EX_NOWAIT | EX_MALLOCOK ))
		printf("%s: can't free region\n",__func__);
	km_free((void *)map->va, PAGE_SIZE, &kv_any, &kp_none);
	free(map, M_AGP, sizeof(*map));
}

int
agp_map_subregion(struct agp_map *map, bus_size_t offset, bus_size_t size,
    bus_space_handle_t *bshp)
{
	return (_bus_space_map(map->bst, map->addr + offset, size,
	    map->flags, bshp));
}

void
agp_unmap_subregion(struct agp_map *map, bus_space_handle_t bsh,
    bus_size_t size)
{
	return (_bus_space_unmap(map->bst, bsh, size, NULL));
}

void
agp_map_atomic(struct agp_map *map, bus_size_t offset,
    bus_space_handle_t *bshp)
{
	int pmap_flags = PMAP_NOCACHE;
	paddr_t pa;

	KASSERT((offset & PGOFSET) == 0);

	if (map->flags & BUS_SPACE_MAP_CACHEABLE)
		pmap_flags = 0;
	else if (map->flags & BUS_SPACE_MAP_PREFETCHABLE)
		pmap_flags = PMAP_WC;

	pa = bus_space_mmap(map->bst, map->addr, offset, 0, 0);
	pmap_kenter_pa(map->va, pa | pmap_flags, PROT_READ | PROT_WRITE);
	pmap_update(pmap_kernel());

	*bshp = (bus_space_handle_t)map->va;
}

void
agp_unmap_atomic(struct agp_map *map, bus_space_handle_t bsh)
{
	KASSERT(bsh == (bus_space_handle_t)map->va);

	pmap_kremove(map->va, PAGE_SIZE);
	pmap_update(pmap_kernel());
}


code:
/*	$OpenBSD: ali1543.c,v 1.4 2014/09/14 14:17:23 jsg Exp $	*/
/*	$NetBSD: ali1543.c,v 1.2 2001/09/13 14:00:52 tshiozak Exp $	*/

/*
 * Copyright (c) 2001
 *       HAYAKAWA Koichi.  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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * 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.
 */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 */

/* HAYAKAWA Koichi wrote ALi 1543 PCI ICU code basing on VIA82C586 driver */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>

#include <i386/pci/pcibiosvar.h>
#include <i386/pci/piixvar.h>

int ali1543_getclink (pciintr_icu_handle_t, int, int *);
int ali1543_get_intr (pciintr_icu_handle_t, int, int *);
int ali1543_set_intr (pciintr_icu_handle_t, int, int);

const struct pciintr_icu ali1543_icu = {
	ali1543_getclink,
	ali1543_get_intr,
	ali1543_set_intr,
	piix_get_trigger,
	piix_set_trigger,
};

/*
 * Linux source code (linux/arch/i386/kernel/pci-irq.c) says that the
 * irq order of ALi PCI ICU is shuffled.
 */
const static int ali1543_intr_shuffle_get[16] = {
	0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15
};
const static int ali1543_intr_shuffle_set[16] = {
	0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15
};

#define ALI1543_IRQ_MASK		0xdefa

#define ALI1543_LEGAL_LINK(link)	(((link) >= 0) && ((link) <= 7))
#define ALI1543_LEGAL_IRQ(irq)		((1 << (irq)) & ALI1543_IRQ_MASK)

#define ALI1543_INTR_CFG_REG		0x48

#define ALI1543_INTR_PIRQA_SHIFT	0
#define ALI1543_INTR_PIRQA_MASK		0x0000000f
#define ALI1543_INTR_PIRQB_SHIFT	4
#define ALI1543_INTR_PIRQB_MASK		0x000000f0
#define ALI1543_INTR_PIRQC_SHIFT	8
#define ALI1543_INTR_PIRQC_MASK		0x00000f00
#define ALI1543_INTR_PIRQD_SHIFT	12
#define ALI1543_INTR_PIRQD_MASK		0x0000f000

#define ALI1543_INTR_PIRQ_SHIFT(clink)	((clink)*4)
#define ALI1543_INTR_PIRQ_IRQ(reg, clink)				\
	(((reg) >> ((clink)*4)) & 0x0f)
#define ALI1543_PIRQ(reg, clink)					\
	ali1543_intr_shuffle_get[ALI1543_INTR_PIRQ_IRQ((reg), (clink))]

int
ali1543_init(pci_chipset_tag_t pc, bus_space_tag_t iot, pcitag_t tag,
    pciintr_icu_tag_t *ptagp, pciintr_icu_handle_t *phandp)
{

	if (piix_init(pc, iot, tag, ptagp, phandp) == 0) {
		*ptagp = &ali1543_icu;

		return (0);
	}

	return (1);
}

int
ali1543_getclink(pciintr_icu_handle_t v, int link, int *clinkp)
{

	if (ALI1543_LEGAL_LINK(link - 1)) {
		*clinkp = link - 1;
		return (0);
	}

	return (1);
}

int
ali1543_get_intr(pciintr_icu_handle_t v, int clink, int *irqp)
{
	struct piix_handle *ph = v;
	pcireg_t reg;
	int val;

	if (ALI1543_LEGAL_LINK(clink) == 0)
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, ALI1543_INTR_CFG_REG);
#ifdef DEBUG_1543
	printf("ali1543: PIRQ reg 0x%08x\n", reg); /* XXX debug */
#endif /* DEBUG_1543 */
	val = ALI1543_PIRQ(reg, clink);
	*irqp = (val == 0) ?
	    I386_PCI_INTERRUPT_LINE_NO_CONNECTION : val;

	return (0);
}

int
ali1543_set_intr(pciintr_icu_handle_t v, int clink, int irq)
{
	struct piix_handle *ph = v;
	int shift, val;
	pcireg_t reg;

	if (ALI1543_LEGAL_LINK(clink) == 0 || ALI1543_LEGAL_IRQ(irq) == 0)
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, ALI1543_INTR_CFG_REG);
	ali1543_get_intr(v, clink, &val);
	shift = ALI1543_INTR_PIRQ_SHIFT(clink);
	reg &= ~(0x0f << shift);
	reg |= (ali1543_intr_shuffle_set[irq] << shift);
	pci_conf_write(ph->ph_pc, ph->ph_tag, ALI1543_INTR_CFG_REG, reg);
	if (ali1543_get_intr(v, clink, &val) != 0 || val != irq)
		return (1);

	return (0);
}


code:
/*	$OpenBSD: amd756.c,v 1.5 2008/06/26 05:42:11 ray Exp $	*/
/*	$NetBSD$	*/

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * 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.
 */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 */

/*
 * Support for the Advanced Micro Devices AMD756 Peripheral Bus Controller.
 */

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

#include <machine/intr.h>
#include <machine/bus.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>

#include <i386/pci/pcibiosvar.h>
#include <i386/pci/amd756reg.h>

struct viper_handle {
	bus_space_tag_t ph_iot;
	bus_space_handle_t ph_regs_ioh;
	pci_chipset_tag_t ph_pc;
	pcitag_t ph_tag;
};

int amd756_getclink(pciintr_icu_handle_t, int, int *);
int amd756_get_intr(pciintr_icu_handle_t, int, int *);
int amd756_set_intr(pciintr_icu_handle_t, int, int);
int amd756_get_trigger(pciintr_icu_handle_t, int, int *);
int amd756_set_trigger(pciintr_icu_handle_t, int, int);
#ifdef VIPER_DEBUG
static void amd756_pir_dump(struct viper_handle *);
#endif

const struct pciintr_icu amd756_pci_icu = {
	amd756_getclink,
	amd756_get_intr,
	amd756_set_intr,
	amd756_get_trigger,
	amd756_set_trigger,
};

int
amd756_init(pci_chipset_tag_t pc, bus_space_tag_t iot, pcitag_t tag,
    pciintr_icu_tag_t *ptagp, pciintr_icu_handle_t *phandp)
{
	struct viper_handle *ph;

	ph = malloc(sizeof(*ph), M_DEVBUF, M_NOWAIT);
	if (ph == NULL)
		return (1);

	ph->ph_iot = iot;
	ph->ph_pc = pc;
	ph->ph_tag = tag;

	*ptagp = &amd756_pci_icu;
	*phandp = ph;

#ifdef VIPER_DEBUG
	amd756_pir_dump(ph);
#endif

	return 0;
}

int
amd756_getclink(pciintr_icu_handle_t v, int link, int *clinkp)
{
	if (AMD756_LEGAL_LINK(link - 1) == 0)
		return (1);

	*clinkp = link - 1;
	return (0);
}

int
amd756_get_intr(pciintr_icu_handle_t v, int clink, int *irqp)
{
	struct viper_handle *ph = v;
	pcireg_t reg;
	int val;

	if (AMD756_LEGAL_LINK(clink) == 0)
		return (1);

	reg = AMD756_GET_PIIRQSEL(ph);
	val = (reg >> (4*clink)) & 0x0f;
	*irqp = (val == 0) ?
	    I386_PCI_INTERRUPT_LINE_NO_CONNECTION : val;

	return (0);
}

int
amd756_set_intr(pciintr_icu_handle_t v, int clink, int irq)
{
	struct viper_handle *ph = v;
	int val;
	pcireg_t reg;

	if (AMD756_LEGAL_LINK(clink) == 0 || AMD756_LEGAL_IRQ(irq) == 0)
		return (1);

	reg = AMD756_GET_PIIRQSEL(ph);
	amd756_get_intr(v, clink, &val);
	reg &= ~(0x000f << (4*clink));
	reg |= irq << (4*clink);
	AMD756_SET_PIIRQSEL(ph, reg);

	return 0;
}

int
amd756_get_trigger(pciintr_icu_handle_t v, int irq, int *triggerp)
{
	struct viper_handle *ph = v;
	int i, pciirq;
	pcireg_t reg;

	if (AMD756_LEGAL_IRQ(irq) == 0)
		return (1);

	for (i = 0; i <= 3; i++) {
		amd756_get_intr(v, i, &pciirq);
		if (pciirq == irq) {
			reg = AMD756_GET_EDGESEL(ph);
			if (reg & (1 << i))
				*triggerp = IST_EDGE;
			else
				*triggerp = IST_LEVEL;
			break;
		}
	}

	return 0;
}

int
amd756_set_trigger(pciintr_icu_handle_t v, int irq, int trigger)
{
	struct viper_handle *ph = v;
	int i, pciirq;
	pcireg_t reg;

	if (AMD756_LEGAL_IRQ(irq) == 0)
		return (1);

	for (i = 0; i <= 3; i++) {
		amd756_get_intr(v, i, &pciirq);
		if (pciirq == irq) {
			reg = AMD756_GET_PIIRQSEL(ph);
			if (trigger == IST_LEVEL)
				reg &= ~(1 << (4*i));
			else
				reg |= 1 << (4*i);
			AMD756_SET_PIIRQSEL(ph, reg);
			break;
		}
	}

	return (0);
}

#ifdef VIPER_DEBUG
static void
amd756_pir_dump(struct viper_handle *ph)
{
	int a, b;

	printf ("VIPER PCI INTERRUPT ROUTING REGISTERS:\n");

	a = AMD756_GET_EDGESEL(ph);
	b = AMD756_GET_PIIRQSEL(ph);

	printf ("TRIGGER: %02x, ROUTING: %04x\n", a, b);
}
#endif


code:
/*	$OpenBSD: amd756reg.h,v 1.1 2000/11/07 18:21:22 mickey Exp $	*/
/*	$NetBSD$	*/

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 */

/*
 * Register definitions for the AMD756 Peripheral Bus Controller.
 */

/*
 * Edge Triggered Interrupt Select register. (0x54)
 * bits 7-4: reserved
 * bit 3: Edge Triggered Interrupt Select for PCI Interrupt D
 * bit 2: Edge Triggered Interrupt Select for PCI Interrupt C
 * bit 1: Edge Triggered Interrupt Select for PCI Interrupt B
 * bit 0: Edge Triggered Interrupt Select for PCI Interrupt A
 *   0 = active Low and level triggered
 *   1 = active High and edge triggered
 *
 * PIRQ Select register. (0x56-57)
 * bits 15-12: PIRQD# Select
 * bits 11-8:  PIRQD# Select
 * bits 7-4:   PIRQD# Select
 * bits 3-0:   PIRQD# Select
 *   0000: Reserved  0100: IRQ4      1000: Reserved  1100: IRQ12
 *   0001: IRQ1      0101: IRQ5      1001: IRQ9      1101: Reserved
 *   0010: Reserved  0110: IRQ6      1010: IRQ10     1110: IRQ14
 *   0011: IRQ3      0111: IRQ7      1011: IRQ11     1111: IRQ15
 */
#define AMD756_CFG_PIR		0x54

#define AMD756_GET_EDGESEL(ph)						\
	(pci_conf_read((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR) & 0xff)

#define AMD756_GET_PIIRQSEL(ph)						\
	(pci_conf_read((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR) >> 16)

#define AMD756_SET_EDGESEL(ph, n)					\
	pci_conf_write((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR,	\
	(pci_conf_read((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR)	\
	    & 0xffff0000) | (n))

#define AMD756_SET_PIIRQSEL(ph, n)					\
	pci_conf_write((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR,	\
	(pci_conf_read((ph)->ph_pc, (ph)->ph_tag, AMD756_CFG_PIR)	\
	    & 0x000000ff) | ((n) << 16))

#define AMD756_PIRQ_MASK	0xdefa
#define		AMD756_LEGAL_LINK(link) ((link) >= 0 && (link) <= 3)
#define AMD756_LEGAL_IRQ(irq) \
	((irq) >= 0 && (irq) <= 15 && ((1 << (irq)) & AMD756_PIRQ_MASK) != 0)


code:
/*	$OpenBSD: elan520.c,v 1.21 2014/12/10 12:27:56 mikeb Exp $	*/
/*	$NetBSD: elan520.c,v 1.4 2002/10/02 05:47:15 thorpej Exp $	*/

/*-
 * Copyright (c) 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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.
 */

/*
 * Device driver for the AMD Elan SC520 System Controller.  This attaches
 * where the "pchb" driver might normally attach, and provides support for
 * extra features on the SC520, such as the watchdog timer and GPIO.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/timetc.h>

#include <machine/bus.h>

#include <machine/cpufunc.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <dev/gpio/gpiovar.h>

#include <arch/i386/pci/elan520reg.h>

struct elansc_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_memt;
	bus_space_handle_t	sc_memh;

	/* GPIO interface */
	struct gpio_chipset_tag sc_gpio_gc;
	gpio_pin_t sc_gpio_pins[ELANSC_PIO_NPINS];

	/* GP timer */
	struct timecounter	sc_tc;
} *elansc;

int	elansc_match(struct device *, void *, void *);
void	elansc_attach(struct device *, struct device *, void *);
int	elansc_activate(struct device *, int);
void	elansc_update_cpuspeed(void);
void	elansc_setperf(int);
int	elansc_cpuspeed(int *);

void	elansc_wdogctl(struct elansc_softc *, int, uint16_t);
#define elansc_wdogctl_reset(sc)	elansc_wdogctl(sc, 1, 0)
#define elansc_wdogctl_write(sc, val)	elansc_wdogctl(sc, 0, val)
int	elansc_wdogctl_cb(void *, int);

int	elansc_gpio_pin_read(void *, int);
void	elansc_gpio_pin_write(void *, int, int);
void	elansc_gpio_pin_ctl(void *, int, int);

u_int	elansc_tc_read(struct timecounter *);

struct cfattach elansc_ca = {
	sizeof(struct elansc_softc), elansc_match, elansc_attach,
	NULL, elansc_activate
};

struct cfdriver elansc_cd = {
	NULL, "elansc", DV_DULL
};

int
elansc_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_ELANSC520)
		return (10);	/* beat pchb */

	return (0);
}

static const char *elansc_speeds[] = {
	"(reserved 00)",
	"100MHz",
	"133MHz",
	"(reserved 11)",
};

#define RSTBITS "\20\x07SCP\x06HRST\x05SRST\x04WDT\x03SD\x02PRGRST\x01PWRGOOD"

void
elansc_attach(struct device *parent, struct device *self, void *aux)
{
	struct elansc_softc *sc = (void *) self;
	struct pci_attach_args *pa = aux;
	struct gpiobus_attach_args gba;
	struct timecounter *tc;
	uint16_t rev, data;
	uint8_t ressta, cpuctl, tmr;
	int pin, reg, shift;

	sc->sc_memt = pa->pa_memt;
	if (bus_space_map(sc->sc_memt, MMCR_BASE_ADDR, NBPG, 0,
	    &sc->sc_memh) != 0) {
		printf(": unable to map registers\n");
		return;
	}

	rev = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_REVID);
	cpuctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_CPUCTL);
	ressta = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA);

	printf(": product %d stepping %d.%d, CPU clock %s, reset %b\n",
	    (rev & REVID_PRODID) >> REVID_PRODID_SHIFT,
	    (rev & REVID_MAJSTEP) >> REVID_MAJSTEP_SHIFT,
	    (rev & REVID_MINSTEP),
	    elansc_speeds[cpuctl & CPUCTL_CPU_CLK_SPD_MASK],
	    ressta, RSTBITS);

	/*
	 * Determine cause of the last reset, and issue a warning if it
	 * was due to watchdog expiry.
	 */
	if (ressta & RESSTA_WDT_RST_DET)
		printf("%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
		    sc->sc_dev.dv_xname);
	bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA, ressta);

	/* Set up the watchdog registers with some defaults. */
	elansc_wdogctl_write(sc, WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);

	/* ...and clear it. */
	elansc_wdogctl_reset(sc);

	wdog_register(elansc_wdogctl_cb, sc);
	elansc = sc;
	cpu_setperf = elansc_setperf;
	cpu_cpuspeed = elansc_cpuspeed;
	elansc_update_cpuspeed();

	/* Initialize GPIO pins array */
	for (pin = 0; pin < ELANSC_PIO_NPINS; pin++) {
		sc->sc_gpio_pins[pin].pin_num = pin;
		sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
		    GPIO_PIN_OUTPUT;

		/* Read initial state */
		reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
		shift = pin % 16;
		data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
		if ((data & (1 << shift)) == 0)
			sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_INPUT;
		else
			sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_OUTPUT;
		if (elansc_gpio_pin_read(sc, pin) == 0)
			sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW;
		else
			sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH;
	}

	/* Create controller tag */
	sc->sc_gpio_gc.gp_cookie = sc;
	sc->sc_gpio_gc.gp_pin_read = elansc_gpio_pin_read;
	sc->sc_gpio_gc.gp_pin_write = elansc_gpio_pin_write;
	sc->sc_gpio_gc.gp_pin_ctl = elansc_gpio_pin_ctl;

	gba.gba_name = "gpio";
	gba.gba_gc = &sc->sc_gpio_gc;
	gba.gba_pins = sc->sc_gpio_pins;
	gba.gba_npins = ELANSC_PIO_NPINS;

	/* Attach GPIO framework */
	config_found(&sc->sc_dev, &gba, gpiobus_print);

	/* Disable GP1/2, clear the current count, and set the period to max */
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
		GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
		GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT, 0);
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1MAXCMPA, 0);

	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
		GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT, 0);
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2MAXCMPA, 0);

	tmr = bus_space_read_1(sc->sc_memt, sc->sc_memh, SWTMRCFG);

	/* Enable GP1/2 */
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
		GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
		GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
	bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
		GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);

	/* Attach timer */
	tc = &sc->sc_tc;
	tc->tc_get_timecount = elansc_tc_read;
	tc->tc_poll_pps = NULL;
	tc->tc_counter_mask = ~0;
	tc->tc_frequency = (tmr & 1) ? (33000000 / 4) : (33333333 / 4);
	tc->tc_name = sc->sc_dev.dv_xname;
	tc->tc_quality = 1000;
	tc->tc_priv = sc;
	tc_init(tc);
}

int
elansc_activate(struct device *self, int act)
{
	switch (act) {
	case DVACT_POWERDOWN:
		wdog_shutdown(self);
		break;
	}

	return (0);
}

u_int
elansc_tc_read(struct timecounter *tc)
{
	struct elansc_softc *sc = tc->tc_priv;
	u_int32_t m1, m2, l;

	do {
		m1 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
		l = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT);
		m2 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
	} while (m1 != m2);

	return ((m1 << 16) | l);
}

void
elansc_wdogctl(struct elansc_softc *sc, int do_reset, uint16_t val)
{
	int s;
	uint8_t echo_mode;

	s = splhigh();

	/* Switch off GP bus echo mode. */
	echo_mode = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO);
	bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO,
	    echo_mode & ~GPECHO_GP_ECHO_ENB);

	if (do_reset) {
		/* Reset the watchdog. */
		bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
		    WDTMRCTL_RESET1);
		bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
		    WDTMRCTL_RESET2);
	} else {
		/* Unlock the register. */
		bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
		    WDTMRCTL_UNLOCK1);
		bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
		    WDTMRCTL_UNLOCK2);

		/* Write the value. */
		bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
		   val);
	}

	/* Switch GP bus echo mode back. */
	bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO, echo_mode);

	splx(s);
}

static const struct {
	int	period;		/* whole seconds */
	uint16_t exp;		/* exponent select */
} elansc_wdog_periods[] = {
	{ 1,	WDTMRCTL_EXP_SEL25 },
	{ 2,	WDTMRCTL_EXP_SEL26 },
	{ 4,	WDTMRCTL_EXP_SEL27 },
	{ 8,	WDTMRCTL_EXP_SEL28 },
	{ 16,	WDTMRCTL_EXP_SEL29 },
	{ 32,	WDTMRCTL_EXP_SEL30 },
};

int
elansc_wdogctl_cb(void *self, int period)
{
	struct elansc_softc *sc = self;
	int i;

	if (period == 0) {
		elansc_wdogctl_write(sc,
		    WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
	} else {
		for (i = 0; i < (sizeof(elansc_wdog_periods) /
		    sizeof(elansc_wdog_periods[0])) - 1; i++)
			if (elansc_wdog_periods[i].period >= period)
				break;
		period = elansc_wdog_periods[i].period;
		elansc_wdogctl_write(sc, WDTMRCTL_ENB |
		    WDTMRCTL_WRST_ENB | elansc_wdog_periods[i].exp);
		elansc_wdogctl_reset(sc);
	}
	return (period);
}

void
elansc_update_cpuspeed(void)
{
	static const int elansc_mhz[] = { 0, 100, 133, 999 };
	uint8_t cpuctl;

	cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
	    MMCR_CPUCTL);
	cpuspeed = elansc_mhz[cpuctl & CPUCTL_CPU_CLK_SPD_MASK];
}

void
elansc_setperf(int level)
{
	uint32_t eflags;
	uint8_t cpuctl, speed;

	level = (level > 50) ? 100 : 0;

	cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
	    MMCR_CPUCTL);
	speed = (level == 100) ? 2 : 1;
	if ((cpuctl & CPUCTL_CPU_CLK_SPD_MASK) == speed)
		return;

	eflags = read_eflags();
	disable_intr();
	bus_space_write_1(elansc->sc_memt, elansc->sc_memh, MMCR_CPUCTL,
	    (cpuctl & ~CPUCTL_CPU_CLK_SPD_MASK) | speed);
	enable_intr();
	write_eflags(eflags);

	elansc_update_cpuspeed();
}

int
elansc_cpuspeed(int *freq)
{
	*freq = cpuspeed;
	return (0);
}

int
elansc_gpio_pin_read(void *arg, int pin)
{
	struct elansc_softc *sc = arg;
	int reg, shift;
	u_int16_t data;

	reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
	shift = pin % 16;
	data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);

	return ((data >> shift) & 0x1);
}

void
elansc_gpio_pin_write(void *arg, int pin, int value)
{
	struct elansc_softc *sc = arg;
	int reg, shift;
	u_int16_t data;

	reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
	shift = pin % 16;
	data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
	if (value == 0)
		data &= ~(1 << shift);
	else if (value == 1)
		data |= (1 << shift);

	bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
}

void
elansc_gpio_pin_ctl(void *arg, int pin, int flags)
{
	struct elansc_softc *sc = arg;
	int reg, shift;
	u_int16_t data;

	reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
	shift = pin % 16;
	data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
	if (flags & GPIO_PIN_INPUT)
		data &= ~(1 << shift);
	if (flags & GPIO_PIN_OUTPUT)
		data |= (1 << shift);

	bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
}


code:
/*	$OpenBSD: elan520reg.h,v 1.4 2008/06/26 05:42:11 ray Exp $	*/
/*	$NetBSD: elan520reg.h,v 1.1 2002/08/12 01:03:14 thorpej Exp $	*/

/*-
 * Copyright (c) 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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.
 */

/*
 * Register definitions for the AMD Elan SC520 System Controller.
 */

#ifndef _I386_PCI_ELAN520REG_H_
#define	_I386_PCI_ELAN520REG_H_

#define	MMCR_BASE_ADDR		0xfffef000

/*
 * Am5x86 CPU Registers.
 */
#define	MMCR_REVID		0x0000
#define	MMCR_CPUCTL		0x0002

#define	REVID_PRODID		0xff00	/* product ID */
#define	REVID_PRODID_SHIFT	8
#define	REVID_MAJSTEP		0x00f0	/* stepping major */
#define	REVID_MAJSTEP_SHIFT	4
#define	REVID_MINSTEP		0x000f	/* stepping minor */

#define	PRODID_ELAN_SC520	0x00	/* Elan SC520 */

#define	CPUCTL_CPU_CLK_SPD_MASK	0x03	/* CPU clock speed */
#define	CPUCTL_CACHE_WR_MODE	0x10	/* cache mode (0 = wb, 1 = wt) */

/*
 * General Purpose Bus Registers
 */
#define	MMCR_GPECHO		0x0c00	/* GP echo mode */
#define	MMCR_GPCSDW		0x0c01	/* GP chip sel data width */
#define	MMCR_CPCSQUAL		0x0c02	/* GP chip sel qualification */
#define	MMCR_GPCSRT		0x0c08	/* GP chip sel recovery time */
#define	MMCR_GPCSPW		0x0c09	/* GP chip sel pulse width */
#define	MMCR_GPCSOFF		0x0c0a	/* GP chip sel offset */
#define	MMCR_GPRDW		0x0c0b	/* GP read pulse width */
#define	MMCR_GPRDOFF		0x0c0c	/* GP read offset */
#define	MMCR_GPWRW		0x0c0d	/* GP write pulse width */
#define	MMCR_GPWROFF		0x0c0e	/* GP write offset */
#define	MMCR_GPALEW		0x0c0f	/* GPALE pulse width */
#define	MMCR_GPALEOFF		0x0c10	/* GPALE offset */

#define	GPECHO_GP_ECHO_ENB	0x01	/* GP bus echo mode enable */

/*
 * Programmable Input/Output Registers
 */
#define	MMCR_PIOPFS15_0		0x0c20	/* PIO15-PIO0 pin func sel */
#define	MMCR_PIOPFS31_16	0x0c22	/* PIO31-PIO16 pin func sel */
#define	MMCR_CSPFS		0x0c24	/* chip sel pin func sel */
#define	MMCR_CLKSEL		0x0c26	/* clock select */
#define	MMCR_DSCTL		0x0c28	/* drive strength control */
#define	MMCR_PIODIR15_0		0x0c2a	/* PIO15-PIO0 direction */
#define	MMCR_PIODIR31_16	0x0c2c	/* PIO31-PIO16 direction */
#define	MMCR_PIODATA15_0	0x0c30	/* PIO15-PIO0 data */
#define	MMCR_PIODATA31_16	0x0c32	/* PIO31-PIO16 data */
#define	MMCR_PIOSET15_0		0x0c34	/* PIO15-PIO0 set */
#define	MMCR_PIOSET31_16	0x0c36	/* PIO31-PIO16 set */
#define	MMCR_PIOCLR15_0		0x0c38	/* PIO15-PIO0 clear */
#define	MMCR_PIOCLR31_16	0x0c3a	/* PIO31-PIO16 clear */

#define	ELANSC_PIO_NPINS	32	/* total number of PIO pins */

/*
 * Watchdog Timer Registers.
 */
#define	MMCR_WDTMRCTL		0x0cb0	/* watchdog timer control */
#define	MMCR_WDTMRCNTL		0x0cb2	/* watchdog timer count low */
#define	MMCR_WDTMRCNTH		0x0cb4	/* watchdog timer count high */

#define	WDTMRCTL_EXP_SEL_MASK	0x00ff	/* exponent select */
#define	WDTMRCTL_EXP_SEL14	0x0001	/*	496us/492us */
#define	WDTMRCTL_EXP_SEL24	0x0002	/*	508ms/503ms */
#define	WDTMRCTL_EXP_SEL25	0x0004	/*	1.02s/1.01s */
#define	WDTMRCTL_EXP_SEL26	0x0008	/*	2.03s/2.01s */
#define	WDTMRCTL_EXP_SEL27	0x0010	/*	4.07s/4.03s */
#define	WDTMRCTL_EXP_SEL28	0x0020	/*	8.13s/8.05s */
#define	WDTMRCTL_EXP_SEL29	0x0040	/*	16.27s/16.11s */
#define	WDTMRCTL_EXP_SEL30	0x0080	/*	32.54s/32.21s */
#define	WDTMRCTL_IRQ_FLG	0x1000	/* interrupt request */
#define	WDTMRCTL_WRST_ENB	0x4000	/* watchdog timer reset enable */
#define	WDTMRCTL_ENB		0x8000	/* watchdog timer enable */

#define	WDTMRCTL_UNLOCK1	0x3333
#define	WDTMRCTL_UNLOCK2	0xcccc

#define	WDTMRCTL_RESET1		0xaaaa
#define	WDTMRCTL_RESET2		0x5555

/*
 * Reset Generation Registers.
 */
#define	MMCR_SYSINFO		0x0d70	/* system board information */
#define	MMCR_RESCFG		0x0d72	/* reset configuration */
#define	MMCR_RESSTA		0x0d74	/* reset status */

#define	RESCFG_SYS_RST		0x01	/* software system reset */
#define	RESCFG_GP_RST		0x02	/* assert GP bus reset */
#define	RESCFG_PRG_RST_ENB	0x04	/* programmable reset enable */
#define	RESCFG_ICE_ON_RST	0x08	/* enter AMDebug(tm) on reset */

#define	RESSTA_PWRGOOD_DET	0x01	/* POWERGOOD reset detect */
#define	RESSTA_PRGRST_DET	0x02	/* programmable reset detect */
#define	RESSTA_SD_RST_DET	0x04	/* CPU shutdown reset detect */
#define	RESSTA_WDT_RST_DET	0x08	/* watchdog timer reset detect */
#define	RESSTA_ICE_SRST_DET	0x10	/* AMDebug(tm) soft reset detect */
#define	RESSTA_ICE_HRST_DET	0x20	/* AMDebug(tm) soft reset detect */
#define	RESSTA_SCP_RST		0x40	/* SCP reset detect */

/*
 * General-Purpose Timers
 */
#define	SWTMRCFG		0xc64	/* Software Timer Configuration */
#define	GPTMR0CTL		0xc72	/* GP Timer 0 mode/control */
#define	GPTMR0CNT		0xc74	/* GP Timer 0 current count value */
#define	GPTMR0MAXCMPA		0xc76	/* GP Timer 0 maxcount value A */
#define	GPTMR0MAXCMPB		0xc78	/* GP Timer 0 maxcount value B */
#define	GPTMR1CTL		0xc7a	/* GP Timer 1 mode/control */
#define	GPTMR1CNT		0xc7c	/* GP Timer 1 current count value */
#define	GPTMR1MAXCMPA		0xc7e	/* GP Timer 1 maxcount value A */
#define	GPTMR1MAXCMPB		0xc80	/* GP Timer 1 maxcount value B */
#define	GPTMR2CTL		0xc82	/* GP Timer 2 mode/control */
#define	GPTMR2CNT		0xc84	/* GP Timer 2 current count value */
#define	GPTMR2MAXCMPA		0xc8e	/* GP Timer 2 maxcount value A */
#define	GPTMRCTL_CONT_CMP	0x0001	/* GP Timer continuous mode */
#define	GPTMRCTL_RTG		0x0010	/* GP Timer retrigger */
#define GPTMRCTL_PSC_SEL	0x0008	/* GP Timer prescaler */
#define GPTMRCTL_ENB		0x8000	/* GP Timer enable */
#define GPTMRCTL_ENB_WR		0x4000	/* GP Timer enable bit write */

#endif /* _I386_PCI_ELAN520REG_H_ */


code:
/*	$OpenBSD: geodesc.c,v 1.13 2014/12/10 12:27:56 mikeb Exp $	*/

/*
 * Copyright (c) 2003 Markus Friedl <markus@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Geode SC1100 Information Appliance On a Chip
 * http://www.national.com/ds.cgi/SC/SC1100.pdf
 */

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

#include <machine/bus.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <arch/i386/pci/geodescreg.h>

struct geodesc_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

int	geodesc_match(struct device *, void *, void *);
void	geodesc_attach(struct device *, struct device *, void *);
int	geodesc_activate(struct device *, int);
void	sc1100_sysreset(void);

#ifndef SMALL_KERNEL
int	geodesc_wdogctl_cb(void *, int);
#endif /* SMALL_KERNEL */

struct cfattach geodesc_ca = {
	sizeof(struct geodesc_softc), geodesc_match, geodesc_attach,
	NULL, geodesc_activate
};

struct cfdriver geodesc_cd = {
	NULL, "geodesc", DV_DULL
};

u_int   geodesc_get_timecount(struct timecounter *tc);

struct timecounter geodesc_timecounter = {
	geodesc_get_timecount,	/* get_timecount */
	0,			/* no poll_pps */
	0xffffffff,		/* counter_mask */
	27000000,		/* frequency */
	"GEOTSC",		/* name */
	2000			/* quality */
};

int
geodesc_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_SC1100_XBUS ||
	     PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_SCx200_XBUS))
		return (1);
	return (0);
}

#define WDSTSBITS "\20\x04WDRST\x03WDSMI\x02WDINT\x01WDOVF"

void
geodesc_attach(struct device *parent, struct device *self, void *aux)
{
	struct geodesc_softc *sc = (void *) self;
	struct pci_attach_args *pa = aux;
	uint16_t cnfg, cba;
	uint8_t sts, rev, iid;
	pcireg_t reg;
	extern void (*cpuresetfn)(void);

	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, SC1100_F5_SCRATCHPAD);
	sc->sc_iot = pa->pa_iot;
	if (reg == 0 ||
	    bus_space_map(sc->sc_iot, reg, 64, 0, &sc->sc_ioh)) {
		printf(": unable to map registers at 0x%x\n", reg);
		return;
	}
	cba = bus_space_read_2(sc->sc_iot, sc->sc_ioh, GCB_CBA);
	if (cba != reg) {
		printf(": cba mismatch: cba 0x%x != reg 0x%x\n", cba, reg);
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, 64);
		return;
	}
	sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh, GCB_WDSTS);
	cnfg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, GCB_WDCNFG);
	iid = bus_space_read_1(sc->sc_iot, sc->sc_ioh, GCB_IID);
	rev = bus_space_read_1(sc->sc_iot, sc->sc_ioh, GCB_REV);

	printf(": iid %d revision %d wdstatus %b\n", iid, rev, sts, WDSTSBITS);

#ifndef SMALL_KERNEL
	/* setup and register watchdog */
	bus_space_write_2(sc->sc_iot, sc->sc_ioh, GCB_WDTO, 0);
	sts |= WDOVF_CLEAR;
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, GCB_WDSTS, sts);
	cnfg &= ~WDCNFG_MASK;
	cnfg |= WDTYPE1_RESET|WDPRES_DIV_512;
	bus_space_write_2(sc->sc_iot, sc->sc_ioh, GCB_WDCNFG, cnfg);

	wdog_register(geodesc_wdogctl_cb, sc);
#endif /* SMALL_KERNEL */

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GCB_TSCNFG, TSC_ENABLE);
	/* Hook into the kern_tc */
	geodesc_timecounter.tc_priv = sc;
	tc_init(&geodesc_timecounter);

	/* We have a special way to reset the CPU on the SC1100 */
	cpuresetfn = sc1100_sysreset;
}

int
geodesc_activate(struct device *self, int act)
{
	switch (act) {
	case DVACT_POWERDOWN:
#ifndef SMALL_KERNEL
		wdog_shutdown(self);
#endif
		break;
	}

	return (0);
}

#ifndef SMALL_KERNEL
int
geodesc_wdogctl_cb(void *self, int period)
{
	struct geodesc_softc *sc = self;

	if (period > 0x03ff)
		period = 0x03ff;
	bus_space_write_2(sc->sc_iot, sc->sc_ioh, GCB_WDTO, period * 64);
	return (period);
}
#endif /* SMALL_KERNEL */

u_int
geodesc_get_timecount(struct timecounter *tc)
{
	struct geodesc_softc *sc = tc->tc_priv;

	return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, GCB_TSC));
}

void
sc1100_sysreset(void)
{
	/*
	 * Reset AMD Geode SC1100.
	 *
	 * 1) Write PCI Configuration Address Register (0xcf8) to
	 *    select Function 0, Register 0x44: Bridge Configuration,
	 *    GPIO and LPC Configuration Register Space, Reset
	 *    Control Register.
	 *
	 * 2) Write 0xf to PCI Configuration Data Register (0xcfc)
	 *    to reset IDE controller, IDE bus, and PCI bus, and
	 *    to trigger a system-wide reset.
	 *
	 * See AMD Geode SC1100 Processor Data Book, Revision 2.0,
	 * sections 6.3.1, 6.3.2, and 6.4.1.
	 */
	outl(0xCF8, 0x80009044UL);
	outb(0xCFC, 0x0F);
}


code:
/*      $OpenBSD: geodescreg.h,v 1.3 2006/02/01 13:08:12 mickey Exp $     */

/*
 * Copyright (c) 2003 Markus Friedl <markus@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Geode SC1100 Information Appliance On a Chip
 * http://www.national.com/ds.cgi/SC/SC1100.pdf
 */

/* Configuration Space Register Map */

#define SC1100_F5_SCRATCHPAD	0x64

#define	GCB_WDTO	0x0000	/* WATCHDOG Timeout */
#define	GCB_WDCNFG	0x0002	/* WATCHDOG Configuration */
#define	GCB_WDSTS	0x0004	/* WATCHDOG Status */
#define	GCB_TSC		0x0008	/* Cyclecounter at 27MHz */
#define	GCB_TSCNFG	0x000c	/* config for the above */
#define	GCB_IID		0x003c	/* IA On a Chip ID */
#define	GCB_REV		0x003d	/* Revision */
#define	GCB_CBA		0x003e	/* Configuration Base Address */

/* Watchdog */

#define	WD32KPD_ENABLE	0x0000
#define	WD32KPD_DISABLE	0x0100
#define	WDTYPE1_RESET	0x0030
#define	WDTYPE2_RESET	0x00c0
#define	WDPRES_DIV_512	0x0009
#define	WDPRES_DIV_8192	0x000d
#define	WDCNFG_MASK	0x00ff
#define	WDOVF_CLEAR	0x0001

/* cyclecounter */

#define	TSC_ENABLE	0x0200