code:
/*	$OpenBSD: gscbus.c,v 1.30 2010/11/28 20:09:40 miod Exp $	*/

/*
 * Copyright (c) 1998 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.
 */

/* #define GSCDEBUG */

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

#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <machine/cpufunc.h>

#include <hppa/gsc/gscbusvar.h>

int	gscmatch(struct device *, void *, void *);
void	gscattach(struct device *, struct device *, void *);

struct cfattach gsc_ca = {
	sizeof(struct gsc_softc), gscmatch, gscattach
};

struct cfdriver gsc_cd = {
	NULL, "gsc", DV_DULL
};

int
gscmatch(parent, cfdata, aux)
	struct device *parent;
	void *cfdata;
	void *aux;
{
	struct confargs *ca = aux;

	return !strcmp(ca->ca_name, "gsc");
}

void
gscattach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
	struct gsc_softc *sc = (struct gsc_softc *)self;
	struct gsc_attach_args *ga = aux;

	sc->sc_iot = ga->ga_iot;
	sc->sc_ic = ga->ga_ic;

#ifdef USELEDS
	if (machine_ledaddr)
		printf(": %sleds", machine_ledword? "word" : "");
#endif
	printf ("\n");

	sc->sc_ih = cpu_intr_establish(IPL_NESTED, ga->ga_irq,
	    gsc_intr, (void *)sc->sc_ic->gsc_base, sc->sc_dev.dv_xname);

	pdc_scanbus(self, &ga->ga_ca, MAXMODBUS, 0, 0);
}

int
gscprint(aux, pnp)
	void *aux;
	const char *pnp;
{
	struct gsc_attach_args *ga = aux;

	if (pnp)
		printf("%s at %s", ga->ga_name, pnp);
	return (UNCONF);
}

void *
gsc_intr_establish(sc, irq, pri, handler, arg, name)
	struct gsc_softc *sc;
	int pri;
	int irq;
	int (*handler)(void *v);
	void *arg;
	const char *name;
{
	volatile u_int32_t *r = sc->sc_ic->gsc_base;
	void *iv;

	if ((iv = cpu_intr_map(sc->sc_ih, pri, irq, handler, arg, name)))
		r[1] |= (1 << irq);
	else {
#ifdef GSCDEBUG
		printf("%s: attaching irq %d, already occupied\n",
		       sc->sc_dev.dv_xname, irq);
#endif
	}

	return (iv);
}

void
gsc_intr_disestablish(sc, v)
	struct gsc_softc *sc;
	void *v;
{
#if notyet
	volatile u_int32_t *r = sc->sc_ic->gsc_base;

	r[1] &= ~(1 << irq);

	cpu_intr_unmap(sc->sc_ih, v);
#endif
}


code:
/*	$OpenBSD: gscbusvar.h,v 1.14 2010/11/28 20:09:40 miod Exp $	*/

/*
 * Copyright (c) 1998-2004 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.
 */

struct gscbus_ic {
	enum {gsc_unknown = 0, gsc_lasi, gsc_wax, gsc_asp} gsc_type;
	void *gsc_dv;
	volatile void *gsc_base;
};

struct gsc_attach_args {
	struct confargs ga_ca;
#define	ga_name		ga_ca.ca_name
#define	ga_iot		ga_ca.ca_iot
#define	ga_dp		ga_ca.ca_dp
#define	ga_type		ga_ca.ca_type
#define	ga_hpa		ga_ca.ca_hpa
#define	ga_hpamask	ga_ca.ca_hpamask
#define	ga_dmatag	ga_ca.ca_dmatag
#define	ga_irq		ga_ca.ca_irq
#define	ga_pdc_iodc_read	ga_ca.ca_pdc_iodc_read
	struct gscbus_ic *ga_ic;	/* IC pointer */
};

struct gsc_softc {
	struct  device sc_dev;
	void *sc_ih;

	bus_space_tag_t sc_iot;
	struct gscbus_ic *sc_ic;
};

void *gsc_intr_establish(struct gsc_softc *sc, int irq, int pri,
    int (*handler)(void *v), void *arg, const char *name);
void gsc_intr_disestablish(struct gsc_softc *sc, void *v);
int gsc_intr(void *);

int gscprint(void *, const char *);

license: unknown


code:
/*	$OpenBSD: gsckbc.c,v 1.19 2015/05/24 10:57:47 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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 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.
 */

/*
 * Derived from /sys/dev/ic/pckbd.c under the following terms:
 * OpenBSD: pckbc.c,v 1.5 2002/06/09 00:58:03 nordin Exp
 * NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp
 */
/*
 * Copyright (c) 1998
 *	Matthias Drochner.  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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed for the NetBSD Project
 *	by Matthias Drochner.
 * 4. 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.
 */

/*
 * Driver for the PS/2-like keyboard and mouse ports found on 712 and 715
 * models, among others.
 *
 * Contrary to the ``pckbc'' port set found on other arches, the
 * keyboard and mouse port are two separate entities on the snakes, and
 * they are driven by a custom chip not 8042-compatible.
 */

#include "pckbd.h"

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

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

#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>

#include <hppa/gsc/gsckbcreg.h>
#include <dev/ic/pckbcvar.h>

#include <dev/pckbc/pckbdreg.h>	/* constants for probe magic */
#include <dev/pckbc/pckbdvar.h>

int	gsckbc_match(struct device *, void *, void *);
void	gsckbc_attach(struct device *, struct device *, void *);

struct	gsckbc_softc {
	struct pckbc_softc sc_pckbc;

	void *sc_ih;
	int sc_type;
};

struct cfattach gsckbc_ca = {
	sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach
};

struct cfdriver gsckbc_cd = {
	NULL, "gsckbc", DV_DULL
};

/* descriptor for one device command */
struct pckbc_devcmd {
	TAILQ_ENTRY(pckbc_devcmd) next;
	int flags;
#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
#define KBC_CMDFLAG_SLOW 2
	u_char cmd[4];
	int cmdlen, cmdidx, retries;
	u_char response[4];
	int status, responselen, responseidx;
};

/* data per slave device */
struct pckbc_slotdata {
	int polling; /* don't read data port in interrupt handler */
	TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
	TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
#define NCMD 5
	struct pckbc_devcmd cmds[NCMD];
};

#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
/* Force polling mode behaviour for boot -a XXX */
#define IS_POLLING(q)	((q)->polling || cold)

void pckbc_init_slotdata(struct pckbc_slotdata *);
int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t);
int pckbc_submatch(struct device *, void *, void *);
int pckbcprint(void *, const char *);

int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
				  u_char);
void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
				 struct pckbc_devcmd *);

void pckbc_cleanqueue(struct pckbc_slotdata *);
void pckbc_cleanup(void *);
int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
int gsckbcintr(void *);

const char *pckbc_slot_names[] = { "kbd", "mouse" };

#define KBC_DEVCMD_ACK 0xfa
#define KBC_DEVCMD_RESEND 0xfe

#define	KBD_DELAY	DELAY(8)

int
gsckbc_match(struct device *parent, void *match, void *aux)
{
	struct gsc_attach_args *ga = aux;
	bus_space_handle_t ioh;
	u_int8_t rv;

	if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
	    ga->ga_type.iodc_sv_model != HPPA_FIO_GPCIO)
		return (0);

	/* Map the i/o space. */
	if (bus_space_map(ga->ga_ca.ca_iot, ga->ga_ca.ca_hpa,
	    KBMAPSIZE, 0, &ioh))
		return 0;

	rv = bus_space_read_1(ga->ga_ca.ca_iot, ioh, KBIDP);
	bus_space_unmap(ga->ga_ca.ca_iot, ioh, KBMAPSIZE);

	if (rv == PCKBC_KBD_SLOT || rv == PCKBC_AUX_SLOT)
		return (1);	/* keyboard or mouse port */

	return (0);
}

/*
 * Attachment helper functions
 */

/* state machine values */
#define	PROBE_SUCCESS	0
#define	PROBE_TIMEOUT	1
#define	PROBE_RETRANS	2
#define	PROBE_NOACK	3

int probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
int probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
int probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
int probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
int probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh);

#define	PROBE_TRIES	1000

int
probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
{
	int numtries = PROBE_TRIES;

	while (numtries--) {
		if (bus_space_read_1(iot, ioh, KBSTATP) &
		    (KBS_DIB | KBS_TERR | KBS_PERR))
			break;
		DELAY(500);
	}

	if (numtries <= 0)
		return (PROBE_TIMEOUT);

	if (bus_space_read_1(iot, ioh, KBSTATP) & (KBS_PERR | KBS_TERR)) {
		if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
			bus_space_write_1(iot, ioh, KBRESETP, 0xff);
			bus_space_write_1(iot, ioh, KBRESETP, 0x00);
			bus_space_write_1(iot, ioh, KBCMDP,
			    bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
			return (PROBE_TIMEOUT);
		}

		*reply = bus_space_read_1(iot, ioh, KBDATAP);
		if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
			bus_space_write_1(iot, ioh, KBRESETP, 0xff);
			bus_space_write_1(iot, ioh, KBRESETP, 0x00);
			bus_space_write_1(iot, ioh, KBCMDP,
			    bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
			if (probe_sendtmo(iot, ioh, KBR_RESEND))
				return (PROBE_TIMEOUT);
			else
				return (PROBE_RETRANS);
		} else
			return (PROBE_SUCCESS);
	} else {
		*reply = bus_space_read_1(iot, ioh, KBDATAP);
		return (PROBE_SUCCESS);
	}
}

int
probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
{
	int read_status;
	int retrans = KB_MAX_RETRANS;

	do {
		read_status = probe_readtmo(iot, ioh, reply);
	} while ((read_status == PROBE_RETRANS) && retrans--);

	return (read_status);
}

int
probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
{
	int numtries = PROBE_TRIES;

	while (numtries--) {
		if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) == 0)
			break;
		DELAY(500);
	}

	if (numtries <= 0)
		return (1);

	bus_space_write_1(iot, ioh, KBDATAP, cmdbyte);
	bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
	return (0);
}

int
probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
{
	int retranscount;
	int reply;

	for (retranscount = 0; retranscount < KB_MAX_RETRANS; retranscount++) {
		if (probe_sendtmo(iot, ioh, cmdbyte))
			return (PROBE_TIMEOUT);
		if (probe_readretry(iot, ioh, &reply))
			return (PROBE_TIMEOUT);

		switch (reply) {
		case KBR_ACK:
			return (PROBE_SUCCESS);
		case KBR_RESEND:
			break;
		default:
			return (PROBE_NOACK);
		}
	}
	return (PROBE_TIMEOUT);

}

int
probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh)
{
	int status;

	bus_space_write_1(iot, ioh, KBRESETP, 0);
	bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
	DELAY(0x20000);	/* XXX why 0x? */
	bus_space_write_1(iot, ioh, KBCMDP, 0);
	DELAY(20000);
	bus_space_write_1(iot, ioh, KBRESETP, 0);
	bus_space_write_1(iot, ioh, KBCMDP, KBCP_DIAG);
	DELAY(20000);

	status = probe_sendack(iot, ioh, KBC_DISABLE);
	switch (status) {
	case PROBE_TIMEOUT:
		if (bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) {
			bus_space_write_1(iot, ioh, KBRESETP, 0);
			bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
		}
		return (-1);
	case PROBE_NOACK:
		return (-1);
	}

	if (probe_sendack(iot, ioh, KBC_ID) != PROBE_SUCCESS)
		return (-1);

	if (probe_readretry(iot, ioh, &status))
		return (-1);

	switch (status) {
	case KBR_MOUSE_ID:
		return PCKBC_AUX_SLOT;
	case KBR_KBD_ID1:
		if (probe_readretry(iot, ioh, &status))
			return (-1);
		if (status == KBR_KBD_ID2) {
			if (probe_sendack(iot, ioh, KBC_ENABLE) ==
			    PROBE_TIMEOUT) {
				bus_space_write_1(iot, ioh, KBRESETP, 0);
				bus_space_write_1(iot, ioh, KBCMDP,
				    KBCP_ENABLE);
			}
			return PCKBC_KBD_SLOT;
		}
	}
	return (-1);
}

void
gsckbc_attach(struct device *parent, struct device *self, void *aux)
{
	struct gsc_attach_args *ga = aux;
	struct gsckbc_softc *gsc = (void *)self;
	struct pckbc_softc *sc = &gsc->sc_pckbc;
	struct pckbc_internal *t;
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
	int ident;

	iot = ga->ga_ca.ca_iot;

	if (bus_space_map(iot, ga->ga_ca.ca_hpa, KBMAPSIZE, 0, &ioh))
		panic("gsckbc_attach: couldn't map port");

	gsc->sc_type = bus_space_read_1(iot, ioh, KBIDP);

	switch (gsc->sc_type) {
	case PCKBC_KBD_SLOT:
	case PCKBC_AUX_SLOT:
		break;
	default:
		printf(": unknown port type %x\n", gsc->sc_type);
		/* play nice and don't really attach. */
		bus_space_unmap(iot, ioh, KBMAPSIZE);
		return;
	}

	gsc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
	    ga->ga_ca.ca_irq, IPL_TTY, gsckbcintr, sc, sc->sc_dv.dv_xname);
	if (gsc->sc_ih == NULL) {
		printf(": can't establish interrupt\n");
		bus_space_unmap(iot, ioh, KBMAPSIZE);
		return;
	}

	printf("\n");

	t = malloc(sizeof(*t), M_DEVBUF, M_WAITOK | M_ZERO);
	t->t_iot = iot;
	/* XXX it does not make sense to only map two ports here */
	t->t_ioh_d = t->t_ioh_c = ioh;
	t->t_addr = ga->ga_ca.ca_hpa;
	t->t_sc = sc;
	timeout_set(&t->t_cleanup, pckbc_cleanup, t);
	sc->id = t;

	/*
	 * Reset port and probe device, if plugged
	 */
	ident = probe_ident(iot, ioh);
	if (ident != gsc->sc_type) {
		/* don't whine for unplugged ports */
		if (ident != -1)
			printf("%s: expecting device type %d, got %d\n",
			    sc->sc_dv.dv_xname, gsc->sc_type, ident);
	} else {
#if (NPCKBD > 0)
		if (gsc->sc_type == PCKBC_KBD_SLOT &&
		    ga->ga_dp.dp_mod == PAGE0->mem_kbd.pz_dp.dp_mod &&
		    bcmp(ga->ga_dp.dp_bc, PAGE0->mem_kbd.pz_dp.dp_bc, 6) == 0)
			pckbd_cnattach(t);
#endif
		pckbc_attach_slot(sc, gsc->sc_type);
	}
}

/*
 * pckbc-like interfaces
 */

int
pckbc_wait_output(iot, ioh)
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
{
	u_int i;

	for (i = 100000; i; i--) {
		if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD)) {
			KBD_DELAY;
		} else
			return (1);
	}
	return (0);
}

int
pckbc_send_cmd(iot, ioh, val)
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
	u_char val;
{
	if (!pckbc_wait_output(iot, ioh))
		return (0);
	bus_space_write_1(iot, ioh, KBOUTP, val);
	bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
	return (1);
}

/* XXX logic */
int
pckbc_poll_data1(iot, ioh, ioh_c, slot, checkaux)
	bus_space_tag_t iot;
	bus_space_handle_t ioh, ioh_c;
	pckbc_slot_t slot;
	int checkaux;	/* ignored on hppa */
{
	int i;
	u_char stat;

	/* if 1 port read takes 1us (?), this polls for 100ms */
	for (i = 100000; i; i--) {
		stat = bus_space_read_1(iot, ioh, KBSTATP);
		if (stat & KBS_DIB) {
			KBD_DELAY;
			return bus_space_read_1(iot, ioh, KBDATAP);
		}
	}
	return (-1);
}

int
pckbc_send_devcmd(t, slot, val)
	struct pckbc_internal *t;
	pckbc_slot_t slot;
	u_char val;
{
	return pckbc_send_cmd(t->t_iot, t->t_ioh_d, val);
}

int
pckbc_submatch(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct cfdata *cf = match;
	struct pckbc_attach_args *pa = aux;

	if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
	    cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
		return (0);
	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
}

int
pckbc_attach_slot(sc, slot)
	struct pckbc_softc *sc;
	pckbc_slot_t slot;
{
	struct pckbc_internal *t = sc->id;
	struct pckbc_attach_args pa;
	int found;

	pa.pa_tag = t;
	pa.pa_slot = slot;
	found = (config_found_sm((struct device *)sc, &pa,
				 pckbcprint, pckbc_submatch) != NULL);

	if (found && !t->t_slotdata[slot]) {
		t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
					     M_DEVBUF, M_NOWAIT);
		if (t->t_slotdata[slot] == NULL)
			return 0;
		pckbc_init_slotdata(t->t_slotdata[slot]);
	}
	return (found);
}

int
pckbcprint(aux, pnp)
	void *aux;
	const char *pnp;
{
#if 0	/* hppa having devices for each slot, this is barely useful */
	struct pckbc_attach_args *pa = aux;

	if (!pnp)
		printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
#endif
	return (QUIET);
}

void
pckbc_init_slotdata(q)
	struct pckbc_slotdata *q;
{
	int i;
	TAILQ_INIT(&q->cmdqueue);
	TAILQ_INIT(&q->freequeue);

	for (i = 0; i < NCMD; i++) {
		TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
	}
	q->polling = 0;
}

void
pckbc_flush(self, slot)
	pckbc_tag_t self;
	pckbc_slot_t slot;
{
	struct pckbc_internal *t = self;

	pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
}

int
pckbc_poll_data(self, slot)
	pckbc_tag_t self;
	pckbc_slot_t slot;
{
	struct pckbc_internal *t = self;
	struct pckbc_slotdata *q = t->t_slotdata[slot];
	int c;

	c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
	if (c != -1 && q && CMD_IN_QUEUE(q)) {
		/* we jumped into a running command - try to
		 deliver the response */
		if (pckbc_cmdresponse(t, slot, c))
			return (-1);
	}
	return (c);
}

int
pckbc_xt_translation(self)
	pckbc_tag_t self;
{
	/* Translation isn't supported... */
	return (-1);
}

void
pckbc_slot_enable(self, slot, on)
	pckbc_tag_t self;
	pckbc_slot_t slot;
	int on;
{
	/* can't enable slots here as they are different devices */
}

void
pckbc_set_poll(self, slot, on)
	pckbc_tag_t self;
	pckbc_slot_t slot;
	int on;
{
	struct pckbc_internal *t = (struct pckbc_internal *)self;

	t->t_slotdata[slot]->polling = on;

	if (!on) {
                int s;

                /*
                 * If disabling polling on a device that's been configured,
                 * make sure there are no bytes left in the FIFO, holding up
                 * the interrupt line.  Otherwise we won't get any further
                 * interrupts.
                 */
		if (t->t_sc) {
			s = spltty();
			gsckbcintr(t->t_sc);
			splx(s);
		}
	}
}

/*
 * Pass command to device, poll for ACK and data.
 * to be called at spltty()
 */
void
pckbc_poll_cmd1(t, slot, cmd)
	struct pckbc_internal *t;
	pckbc_slot_t slot;
	struct pckbc_devcmd *cmd;
{
	bus_space_tag_t iot = t->t_iot;
	bus_space_handle_t ioh = t->t_ioh_d;
	int i, c = 0;

	while (cmd->cmdidx < cmd->cmdlen) {
		if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
			printf("pckbc_cmd: send error\n");
			cmd->status = EIO;
			return;
		}
		for (i = 10; i; i--) { /* 1s ??? */
			c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
			if (c != -1)
				break;
		}

		if (c == KBC_DEVCMD_ACK) {
			cmd->cmdidx++;
			continue;
		}
		if (c == KBC_DEVCMD_RESEND) {
#ifdef PCKBCDEBUG
			printf("pckbc_cmd: RESEND\n");
#endif
			if (cmd->retries++ < KB_MAX_RETRANS)
				continue;
			else {
#ifdef PCKBCDEBUG
				printf("pckbc: cmd failed\n");
#endif
				cmd->status = EIO;
				return;
			}
		}
		if (c == -1) {
#ifdef PCKBCDEBUG
			printf("pckbc_cmd: timeout\n");
#endif
			cmd->status = EIO;
			return;
		}
#ifdef PCKBCDEBUG
		printf("pckbc_cmd: lost 0x%x\n", c);
#endif
	}

	while (cmd->responseidx < cmd->responselen) {
		if (cmd->flags & KBC_CMDFLAG_SLOW)
			i = 100; /* 10s ??? */
		else
			i = 10; /* 1s ??? */
		while (i--) {
			c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
			if (c != -1)
				break;
		}
		if (c == -1) {
#ifdef PCKBCDEBUG
			printf("pckbc_cmd: no data\n");
#endif
			cmd->status = ETIMEDOUT;
			return;
		} else
			cmd->response[cmd->responseidx++] = c;
	}
}

/* for use in autoconfiguration */
int
pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow)
	pckbc_tag_t self;
	pckbc_slot_t slot;
	u_char *cmd;
	int len, responselen;
	u_char *respbuf;
	int slow;
{
	struct pckbc_devcmd nc;

	if ((len > 4) || (responselen > 4))
		return (EINVAL);

	bzero(&nc, sizeof(nc));
	bcopy(cmd, nc.cmd, len);
	nc.cmdlen = len;
	nc.responselen = responselen;
	nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);

	pckbc_poll_cmd1(self, slot, &nc);

	if (nc.status == 0 && respbuf)
		bcopy(nc.response, respbuf, responselen);

	return (nc.status);
}

/*
 * Clean up a command queue, throw away everything.
 */
void
pckbc_cleanqueue(q)
	struct pckbc_slotdata *q;
{
	struct pckbc_devcmd *cmd;
#ifdef PCKBCDEBUG
	int i;
#endif

	while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
		TAILQ_REMOVE(&q->cmdqueue, cmd, next);
#ifdef PCKBCDEBUG
		printf("pckbc_cleanqueue: removing");
		for (i = 0; i < cmd->cmdlen; i++)
			printf(" %02x", cmd->cmd[i]);
		printf("\n");
#endif
		TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
	}
}

/*
 * Timeout error handler: clean queues and data port.
 * XXX could be less invasive.
 */
void
pckbc_cleanup(self)
	void *self;
{
	struct pckbc_internal *t = self;
	int s;

	printf("pckbc: command timeout\n");

	s = spltty();

	if (t->t_slotdata[PCKBC_KBD_SLOT])
		pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
	if (t->t_slotdata[PCKBC_AUX_SLOT])
		pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);

	while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
		KBD_DELAY;
		(void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
	}

	/* reset KBC? */

	splx(s);
}

/*
 * Pass command to device during normal operation.
 * to be called at spltty()
 */
void
pckbc_start(t, slot)
	struct pckbc_internal *t;
	pckbc_slot_t slot;
{
	struct pckbc_slotdata *q = t->t_slotdata[slot];
	struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);

	if (IS_POLLING(q)) {
		do {
			pckbc_poll_cmd1(t, slot, cmd);
			if (cmd->status)
				printf("pckbc_start: command error\n");

			TAILQ_REMOVE(&q->cmdqueue, cmd, next);
			if (cmd->flags & KBC_CMDFLAG_SYNC)
				wakeup(cmd);
			else {
				timeout_del(&t->t_cleanup);
				TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
			}
			cmd = TAILQ_FIRST(&q->cmdqueue);
		} while (cmd);
		return;
	}

	if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
		printf("pckbc_start: send error\n");
		/* XXX what now? */
		return;
	}
}

/*
 * Handle command responses coming in asynchronously,
 * return nonzero if valid response.
 * to be called at spltty()
 */
int
pckbc_cmdresponse(t, slot, data)
	struct pckbc_internal *t;
	pckbc_slot_t slot;
	u_char data;
{
	struct pckbc_slotdata *q = t->t_slotdata[slot];
	struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);

#ifdef DIAGNOSTIC
	if (!cmd)
		panic("pckbc_cmdresponse: no active command");
#endif
	if (cmd->cmdidx < cmd->cmdlen) {
		if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
			return (0);

		if (data == KBC_DEVCMD_RESEND) {
			if (cmd->retries++ < KB_MAX_RETRANS) {
				/* try again last command */
				goto restart;
			} else {
#ifdef PCKBCDEBUG
				printf("pckbc: cmd failed\n");
#endif
				cmd->status = EIO;
				/* dequeue */
			}
		} else {
			if (++cmd->cmdidx < cmd->cmdlen)
				goto restart;
			if (cmd->responselen)
				return (1);
			/* else dequeue */
		}
	} else if (cmd->responseidx < cmd->responselen) {
		cmd->response[cmd->responseidx++] = data;
		if (cmd->responseidx < cmd->responselen)
			return (1);
		/* else dequeue */
	} else
		return (0);

	/* dequeue: */
	TAILQ_REMOVE(&q->cmdqueue, cmd, next);
	if (cmd->flags & KBC_CMDFLAG_SYNC)
		wakeup(cmd);
	else {
		timeout_del(&t->t_cleanup);
		TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
	}
	if (!CMD_IN_QUEUE(q))
		return (1);
restart:
	pckbc_start(t, slot);
	return (1);
}

/*
 * Put command into the device's command queue, return zero or errno.
 */
int
pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf)
	pckbc_tag_t self;
	pckbc_slot_t slot;
	u_char *cmd;
	int len, responselen, sync;
	u_char *respbuf;
{
	struct pckbc_internal *t = self;
	struct pckbc_slotdata *q = t->t_slotdata[slot];
	struct pckbc_devcmd *nc;
	int s, isactive, res = 0;

	if ((len > 4) || (responselen > 4))
		return (EINVAL);
	s = spltty();
	nc = TAILQ_FIRST(&q->freequeue);
	if (nc) {
		TAILQ_REMOVE(&q->freequeue, nc, next);
	}
	splx(s);
	if (!nc)
		return (ENOMEM);

	bzero(nc, sizeof(*nc));
	bcopy(cmd, nc->cmd, len);
	nc->cmdlen = len;
	nc->responselen = responselen;
	nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);

	s = spltty();

	if (IS_POLLING(q) && sync) {
		/*
		 * XXX We should poll until the queue is empty.
		 * But we don't come here normally, so make
		 * it simple and throw away everything.
		 */
		pckbc_cleanqueue(q);
	}

	isactive = CMD_IN_QUEUE(q);
	TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
	if (!isactive)
		pckbc_start(t, slot);

	if (IS_POLLING(q))
		res = (sync ? nc->status : 0);
	else if (sync) {
		if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
			TAILQ_REMOVE(&q->cmdqueue, nc, next);
			pckbc_cleanup(t);
		} else
			res = nc->status;
	} else
		timeout_add_sec(&t->t_cleanup, 1);

	if (sync) {
		if (respbuf)
			bcopy(nc->response, respbuf, responselen);
		TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
	}

	splx(s);

	return (res);
}

void
pckbc_set_inputhandler(self, slot, func, arg, name)
	pckbc_tag_t self;
	pckbc_slot_t slot;
	pckbc_inputfcn func;
	void *arg;
	char *name;
{
	struct pckbc_internal *t = (struct pckbc_internal *)self;
	struct pckbc_softc *sc = t->t_sc;

	if (slot >= PCKBC_NSLOTS)
		panic("pckbc_set_inputhandler: bad slot %d", slot);

	sc->inputhandler[slot] = func;
	sc->inputarg[slot] = arg;
	sc->subname[slot] = name;
}

int
gsckbcintr(void *v)
{
	struct gsckbc_softc *gsc = v;
	struct pckbc_softc *sc = (struct pckbc_softc *)gsc;
	struct pckbc_internal *t = sc->id;
	pckbc_slot_t slot;
	struct pckbc_slotdata *q;
	int served = 0, data;

	while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
		served = 1;

		slot = gsc->sc_type;
		q = t->t_slotdata[slot];

		if (!q) {
			/* XXX do something for live insertion? */
#ifdef PCKBCDEBUG
			printf("gsckbcintr: no dev for slot %d\n", slot);
#endif
			KBD_DELAY;
			(void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
			continue;
		}

		if (IS_POLLING(q))
			break; /* pckbc_poll_data() will get it */

		KBD_DELAY;
		data = bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);

		if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
			continue;

		if (sc->inputhandler[slot])
			(*sc->inputhandler[slot])(sc->inputarg[slot], data);
#ifdef PCKBCDEBUG
		else
			printf("gsckbcintr: slot %d lost %d\n", slot, data);
#endif
	}

	return (served);
}


code:
/*	$OpenBSD: gsckbcreg.h,v 1.1 2003/01/31 22:50:19 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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 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.
 */

/*
 * Register definitions for the GSC PS/2 compatible keyboard/mouse ports.
 *
 * These definitions attempt to match <dev/ic/i8042reg.h> names although the
 * actual wiring is different.
 */

#define	KBSTATP		12	/* controller status port (I) */
#define	KBS_DIB		0x01	/* data in buffer */
#define	KBS_OCMD	0x02	/* output buffer has command */
#define	KBS_PERR	0x04	/* parity error */
#define	KBS_TERR	0x08	/* transmission error */

#define	KBCMDP		8	/* controller port (O) */
#define	KBCP_ENABLE	0x01	/* enable device */
#define	KBCP_DIAG	0x20	/* diagnostic mode control */

#define	KBDATAP		4	/* data port (I) */
#define	KBOUTP		4	/* data port (O) */

#define	KBIDP		0	/* id port (I) */
#define	ID_KBD		0	/* slot is a keyboard port */
#define	ID_MOUSE	1	/* slot is a mouse port */

#define	KBRESETP	0	/* reset port (O) */

#define	KBMAPSIZE	16	/* size to bus_space_map() */

/*
 * Various command definitions not provided by the existing pckbc code.
 */

#define	KBC_ID		0xF2	/* get device identifier */
#define	KBR_MOUSE_ID	0x00	/* mouse type */
#define	KBR_KBD_ID1	0xAB	/* keyboard type */
#define	KBR_KBD_ID2	0x83

#define	KB_MAX_RETRANS	5	/* maximum number of command retrans attempts */


code:
/*	$OpenBSD: harmony.c,v 1.30 2015/05/11 06:46:21 ratchov Exp $	*/

/*
 * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
 * 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 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.
 */

/*
 * Harmony (CS4215/AD1849 LASI) audio interface.
 */

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

#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <dev/rndvar.h>

#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <machine/bus.h>

#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>
#include <hppa/gsc/harmonyreg.h>
#include <hppa/gsc/harmonyvar.h>

int     harmony_open(void *, int);
void    harmony_close(void *);
int     harmony_query_encoding(void *, struct audio_encoding *);
int     harmony_set_params(void *, int, int, struct audio_params *,
    struct audio_params *);
int     harmony_round_blocksize(void *, int);
int     harmony_commit_settings(void *);
int     harmony_halt_output(void *);
int     harmony_halt_input(void *);
int     harmony_getdev(void *, struct audio_device *);
int     harmony_set_port(void *, mixer_ctrl_t *);
int     harmony_get_port(void *, mixer_ctrl_t *);
int     harmony_query_devinfo(void *addr, mixer_devinfo_t *);
void *  harmony_allocm(void *, int, size_t, int, int);
void    harmony_freem(void *, void *, int);
size_t  harmony_round_buffersize(void *, int, size_t);
int     harmony_get_props(void *);
int     harmony_trigger_output(void *, void *, void *, int,
    void (*intr)(void *), void *, struct audio_params *);
int     harmony_trigger_input(void *, void *, void *, int,
    void (*intr)(void *), void *, struct audio_params *);

struct audio_hw_if harmony_sa_hw_if = {
	harmony_open,
	harmony_close,
	NULL,
	harmony_query_encoding,
	harmony_set_params,
	harmony_round_blocksize,
	harmony_commit_settings,
	NULL,
	NULL,
	NULL,
	NULL,
	harmony_halt_output,
	harmony_halt_input,
	NULL,
	harmony_getdev,
	NULL,
	harmony_set_port,
	harmony_get_port,
	harmony_query_devinfo,
	harmony_allocm,
	harmony_freem,
	harmony_round_buffersize,
	NULL,
	harmony_get_props,
	harmony_trigger_output,
	harmony_trigger_input,
	NULL
};

int harmony_match(struct device *, void *, void *);
void harmony_attach(struct device *, struct device *, void *);
int harmony_intr(void *);
void harmony_intr_enable(struct harmony_softc *);
void harmony_intr_disable(struct harmony_softc *);
u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *);
int harmony_set_gainctl(struct harmony_softc *);
void harmony_reset_codec(struct harmony_softc *);
void harmony_start_cp(struct harmony_softc *);
void harmony_tick_pb(void *);
void harmony_tick_cp(void *);
void harmony_try_more(struct harmony_softc *);

void harmony_acc_tmo(void *);
#define	ADD_CLKALLICA(sc) do {						\
	(sc)->sc_acc <<= 1;						\
	(sc)->sc_acc |= READ_REG((sc), HARMONY_DIAG) & DIAG_CO;		\
	if ((sc)->sc_acc_cnt++ && !((sc)->sc_acc_cnt % 32))		\
		add_true_randomness((sc)->sc_acc_num ^= (sc)->sc_acc);	\
} while(0)

int
harmony_match(parent, match, aux)
	struct device *parent;
	void *match, *aux;
{
	struct gsc_attach_args *ga = aux;
	bus_space_handle_t bh;
	u_int32_t cntl;

	if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) {
		if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 ||
		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB ||
		    ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB ||
		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2) {
			if (bus_space_map(ga->ga_iot, ga->ga_hpa,
			    HARMONY_NREGS, 0, &bh) != 0)
				return (0);
			cntl = bus_space_read_4(ga->ga_iot, bh, HARMONY_ID) &
			    ID_REV_MASK;
			bus_space_unmap(ga->ga_iot, bh, HARMONY_NREGS);
			if (cntl == ID_REV_TS || cntl == ID_REV_NOTS)
				return (1);
		}
	}
	return (0);
}

void
harmony_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct harmony_softc *sc = (struct harmony_softc *)self;
	struct gsc_attach_args *ga = aux;
	u_int8_t rev;
	u_int32_t cntl;
	int i;

	sc->sc_bt = ga->ga_iot;
	sc->sc_dmat = ga->ga_dmatag;

	if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0,
	    &sc->sc_bh) != 0) {
		printf(": couldn't map registers\n");
		return;
	}

	cntl = READ_REG(sc, HARMONY_ID);
	sc->sc_teleshare = (cntl & ID_REV_MASK) == ID_REV_TS;

	if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty),
	    PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg,
	    BUS_DMA_NOWAIT) != 0) {
		printf(": couldn't alloc DMA memory\n");
		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
		return;
	}
	if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1,
	    sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva,
	    BUS_DMA_NOWAIT) != 0) {
		printf(": couldn't map DMA memory\n");
		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
		    sc->sc_empty_rseg);
		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
		return;
	}
	if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1,
	    sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT,
	    &sc->sc_empty_map) != 0) {
		printf(": can't create DMA map\n");
		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
		    sizeof(struct harmony_empty));
		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
		    sc->sc_empty_rseg);
		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
		return;
	}
	if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva,
	    sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) {
		printf(": can't load DMA map\n");
		bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map);
		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
		    sizeof(struct harmony_empty));
		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
		    sc->sc_empty_rseg);
		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
		return;
	}

	sc->sc_playback_empty = 0;
	for (i = 0; i < PLAYBACK_EMPTYS; i++)
		sc->sc_playback_paddrs[i] =
		    sc->sc_empty_map->dm_segs[0].ds_addr +
		    offsetof(struct harmony_empty, playback[i][0]);

	sc->sc_capture_empty = 0;
	for (i = 0; i < CAPTURE_EMPTYS; i++)
		sc->sc_capture_paddrs[i] =
		    sc->sc_empty_map->dm_segs[0].ds_addr +
		    offsetof(struct harmony_empty, playback[i][0]);

	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
	    offsetof(struct harmony_empty, playback[0][0]),
	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);

	(void)gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq,
	    IPL_AUDIO, harmony_intr, sc, sc->sc_dv.dv_xname);

	/* set defaults */
	sc->sc_in_port = HARMONY_IN_LINE;
	sc->sc_out_port = HARMONY_OUT_SPEAKER;
	sc->sc_input_lvl.left = sc->sc_input_lvl.right = 240;
	sc->sc_output_lvl.left = sc->sc_output_lvl.right = 244;
	sc->sc_monitor_lvl.left = sc->sc_monitor_lvl.right = 208;
	sc->sc_outputgain = 0;

	/* reset chip, and push default gain controls */
	harmony_reset_codec(sc);

	cntl = READ_REG(sc, HARMONY_CNTL);
	rev = (cntl & CNTL_CODEC_REV_MASK) >> CNTL_CODEC_REV_SHIFT;
	printf(": rev %u", rev);

	if (sc->sc_teleshare)
		printf(", teleshare");
	printf("\n");

	if ((rev & CS4215_REV_VER) >= CS4215_REV_VER_E)
		sc->sc_hasulinear8 = 1;

	strlcpy(sc->sc_audev.name, ga->ga_name, sizeof(sc->sc_audev.name));
	snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version,
	    "%u.%u;%u", ga->ga_type.iodc_sv_rev,
	    ga->ga_type.iodc_model, ga->ga_type.iodc_revision);
	strlcpy(sc->sc_audev.config, sc->sc_dv.dv_xname,
	    sizeof(sc->sc_audev.config));

	audio_attach_mi(&harmony_sa_hw_if, sc, &sc->sc_dv);

	timeout_set(&sc->sc_acc_tmo, harmony_acc_tmo, sc);
	sc->sc_acc_num = 0xa5a5a5a5;
}

void
harmony_reset_codec(struct harmony_softc *sc)
{
	/* silence */
	WRITE_REG(sc, HARMONY_GAINCTL, GAINCTL_OUTPUT_LEFT_M |
	    GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M);

	/* start reset */
	WRITE_REG(sc, HARMONY_RESET, RESET_RST);

	DELAY(100000);		/* wait at least 0.05 sec */

	harmony_set_gainctl(sc);
	WRITE_REG(sc, HARMONY_RESET, 0);
}

void
harmony_acc_tmo(void *v)
{
	struct harmony_softc *sc = v;

	ADD_CLKALLICA(sc);
	timeout_add(&sc->sc_acc_tmo, 1);
}

/*
 * interrupt handler
 */
int
harmony_intr(vsc)
	void *vsc;
{
	struct harmony_softc *sc = vsc;
	struct harmony_channel *c;
	u_int32_t dstatus;
	int r = 0;

	mtx_enter(&audio_lock);
	ADD_CLKALLICA(sc);

	harmony_intr_disable(sc);

	dstatus = READ_REG(sc, HARMONY_DSTATUS);

	if (dstatus & DSTATUS_PN) {
		struct harmony_dma *d;
		bus_addr_t nextaddr;
		bus_size_t togo;

		r = 1;
		c = &sc->sc_playback;
		d = c->c_current;
		togo = c->c_segsz - c->c_cnt;
		if (togo == 0) {
			nextaddr = d->d_map->dm_segs[0].ds_addr;
			c->c_cnt = togo = c->c_blksz;
		} else {
			nextaddr = c->c_lastaddr;
			if (togo > c->c_blksz)
				togo = c->c_blksz;
			c->c_cnt += togo;
		}

		bus_dmamap_sync(sc->sc_dmat, d->d_map,
		    nextaddr - d->d_map->dm_segs[0].ds_addr,
		    c->c_blksz, BUS_DMASYNC_PREWRITE);

		WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
		SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
		c->c_lastaddr = nextaddr + togo;
		harmony_try_more(sc);
	}

	dstatus = READ_REG(sc, HARMONY_DSTATUS);

	if (dstatus & DSTATUS_RN) {
		c = &sc->sc_capture;
		r = 1;
		harmony_start_cp(sc);
		if (sc->sc_capturing && c->c_intr != NULL)
			(*c->c_intr)(c->c_intrarg);
	}

	if (READ_REG(sc, HARMONY_OV) & OV_OV) {
		sc->sc_ov = 1;
		WRITE_REG(sc, HARMONY_OV, 0);
	} else
		sc->sc_ov = 0;

	harmony_intr_enable(sc);
	mtx_leave(&audio_lock);
	return (r);
}

void
harmony_intr_enable(struct harmony_softc *sc)
{
	WRITE_REG(sc, HARMONY_DSTATUS, DSTATUS_IE);
	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
}

void
harmony_intr_disable(struct harmony_softc *sc)
{
	WRITE_REG(sc, HARMONY_DSTATUS, 0);
	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
}

int
harmony_open(void *vsc, int flags)
{
	struct harmony_softc *sc = vsc;

	if (sc->sc_open)
		return (EBUSY);
	sc->sc_open = 1;
	return (0);
}

void
harmony_close(void *vsc)
{
	struct harmony_softc *sc = vsc;

	/* XXX: not useful, halt_*() already called */
	harmony_halt_input(sc);
	harmony_halt_output(sc);
	harmony_intr_disable(sc);
	sc->sc_open = 0;
}

int
harmony_query_encoding(void *vsc, struct audio_encoding *fp)
{
	struct harmony_softc *sc = vsc;
	int err = 0;

	switch (fp->index) {
	case 0:
		strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
		fp->encoding = AUDIO_ENCODING_ULAW;
		fp->precision = 8;
		fp->flags = 0;
		break;
	case 1:
		strlcpy(fp->name, AudioEalaw, sizeof fp->name);
		fp->encoding = AUDIO_ENCODING_ALAW;
		fp->precision = 8;
		fp->flags = 0;
		break;
	case 2:
		strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
		fp->precision = 16;
		fp->flags = 0;
		break;
	case 3:
		if (sc->sc_hasulinear8) {
			strlcpy(fp->name, AudioEulinear, sizeof fp->name);
			fp->encoding = AUDIO_ENCODING_ULINEAR;
			fp->precision = 8;
			fp->flags = 0;
			break;
		}
		/*FALLTHROUGH*/
	default:
		err = EINVAL;
	}
	fp->bps = AUDIO_BPS(fp->precision);
	fp->msb = 1;
	return (err);
}

int
harmony_set_params(void *vsc, int setmode, int usemode,
    struct audio_params *p, struct audio_params *r)
{
	struct harmony_softc *sc = vsc;
	u_int32_t bits;

	switch (p->encoding) {
	case AUDIO_ENCODING_ULAW:
		bits = CNTL_FORMAT_ULAW;
		p->precision = 8;
		break;
	case AUDIO_ENCODING_ALAW:
		bits = CNTL_FORMAT_ALAW;
		p->precision = 8;
		break;
	case AUDIO_ENCODING_SLINEAR_BE:
		if (p->precision == 16) {
			bits = CNTL_FORMAT_SLINEAR16BE;
			break;
		}
		return (EINVAL);
	case AUDIO_ENCODING_ULINEAR_LE:
	case AUDIO_ENCODING_ULINEAR_BE:
		if (p->precision == 8) {
			bits = CNTL_FORMAT_ULINEAR8;
			break;
		}
		return (EINVAL);
	default:
		return (EINVAL);
	}

	if (sc->sc_outputgain)
		bits |= CNTL_OLB;

	if (p->channels == 1)
		bits |= CNTL_CHANS_MONO;
	else if (p->channels == 2)
		bits |= CNTL_CHANS_STEREO;
	else
		return (EINVAL);

	r->sample_rate = p->sample_rate;
	r->encoding = p->encoding;
	r->precision = p->precision;
	p->bps = AUDIO_BPS(p->precision);
	r->bps = AUDIO_BPS(r->precision);
	p->msb = r->msb = 1;

	bits |= harmony_speed_bits(sc, &p->sample_rate);
	sc->sc_cntlbits = bits;
	sc->sc_need_commit = 1;

	return (0);
}

int
harmony_round_blocksize(void *vsc, int blk)
{
	return (HARMONY_BUFSIZE);
}

int
harmony_commit_settings(void *vsc)
{
	struct harmony_softc *sc = vsc;
	u_int32_t reg;
	u_int8_t quietchar;
	int i;

	if (sc->sc_need_commit == 0)
		return (0);

	harmony_intr_disable(sc);

	for (;;) {
		reg = READ_REG(sc, HARMONY_DSTATUS);
		if ((reg & (DSTATUS_PC | DSTATUS_RC)) == 0)
			break;
	}

	/* Setting some bits in gainctl requires a reset */
	harmony_reset_codec(sc);

	/* set the silence character based on the encoding type */
	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
	    offsetof(struct harmony_empty, playback[0][0]),
	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE);
	switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) {
	case CNTL_FORMAT_ULAW:
		quietchar = 0x7f;
		break;
	case CNTL_FORMAT_ALAW:
		quietchar = 0x55;
		break;
	case CNTL_FORMAT_SLINEAR16BE:
	case CNTL_FORMAT_ULINEAR8:
	default:
		quietchar = 0;
		break;
	}
	for (i = 0; i < PLAYBACK_EMPTYS; i++)
		memset(&sc->sc_empty_kva->playback[i][0],
		    quietchar, HARMONY_BUFSIZE);
	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
	    offsetof(struct harmony_empty, playback[0][0]),
	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);

	for (;;) {
		/* Wait for it to come out of control mode */
		reg = READ_REG(sc, HARMONY_CNTL);
		if ((reg & CNTL_C) == 0)
			break;
	}

	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL,
	    sc->sc_cntlbits | CNTL_C);

	for (;;) {
		/* Wait for it to come out of control mode */
		reg = READ_REG(sc, HARMONY_CNTL);
		if ((reg & CNTL_C) == 0)
			break;
	}

	sc->sc_need_commit = 0;

	if (sc->sc_playing || sc->sc_capturing)
		harmony_intr_enable(sc);

	return (0);
}

int
harmony_halt_output(void *vsc)
{
	struct harmony_softc *sc = vsc;

	/* XXX: disable interrupts */
	sc->sc_playing = 0;
	return (0);
}

int
harmony_halt_input(void *vsc)
{
	struct harmony_softc *sc = vsc;

	/* XXX: disable interrupts */
	sc->sc_capturing = 0;
	return (0);
}

int
harmony_getdev(void *vsc, struct audio_device *retp)
{
	struct harmony_softc *sc = vsc;

	*retp = sc->sc_audev;

	return (0);
}

int
harmony_set_port(void *vsc, mixer_ctrl_t *cp)
{
	struct harmony_softc *sc = vsc;
	int err = EINVAL;

	switch (cp->dev) {
	case HARMONY_PORT_INPUT_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels == 1)
			sc->sc_input_lvl.left = sc->sc_input_lvl.right =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
		else if (cp->un.value.num_channels == 2) {
			sc->sc_input_lvl.left =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
			sc->sc_input_lvl.right =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
		} else
			break;
		sc->sc_need_commit = 1;
		err = 0;
		break;
	case HARMONY_PORT_OUTPUT_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels == 1)
			sc->sc_output_lvl.left = sc->sc_output_lvl.right =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
		else if (cp->un.value.num_channels == 2) {
			sc->sc_output_lvl.left =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
			sc->sc_output_lvl.right =
			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
		} else
			break;
		sc->sc_need_commit = 1;
		err = 0;
		break;
	case HARMONY_PORT_OUTPUT_GAIN:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		sc->sc_outputgain = cp->un.ord ? 1 : 0;
		err = 0;
		break;
	case HARMONY_PORT_MONITOR_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels != 1)
			break;
		sc->sc_monitor_lvl.left = sc->sc_input_lvl.right =
		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
		sc->sc_need_commit = 1;
		err = 0;
		break;
	case HARMONY_PORT_RECORD_SOURCE:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		if (cp->un.ord != HARMONY_IN_LINE &&
		    cp->un.ord != HARMONY_IN_MIC)
			break;
		sc->sc_in_port = cp->un.ord;
		err = 0;
		sc->sc_need_commit = 1;
		break;
	case HARMONY_PORT_OUTPUT_SOURCE:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		if (cp->un.ord != HARMONY_OUT_LINE &&
		    cp->un.ord != HARMONY_OUT_SPEAKER &&
		    cp->un.ord != HARMONY_OUT_HEADPHONE)
			break;
		sc->sc_out_port = cp->un.ord;
		err = 0;
		sc->sc_need_commit = 1;
		break;
	}

	return (err);
}

int
harmony_get_port(void *vsc, mixer_ctrl_t *cp)
{
	struct harmony_softc *sc = vsc;
	int err = EINVAL;

	switch (cp->dev) {
	case HARMONY_PORT_INPUT_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels == 1) {
			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
			    sc->sc_input_lvl.left;
		} else if (cp->un.value.num_channels == 2) {
			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
			    sc->sc_input_lvl.left;
			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
			    sc->sc_input_lvl.right;
		} else
			break;
		err = 0;
		break;
	case HARMONY_PORT_INPUT_OV:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		cp->un.ord = sc->sc_ov ? 1 : 0;
		err = 0;
		break;
	case HARMONY_PORT_OUTPUT_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels == 1) {
			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
			    sc->sc_output_lvl.left;
		} else if (cp->un.value.num_channels == 2) {
			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
			    sc->sc_output_lvl.left;
			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
			    sc->sc_output_lvl.right;
		} else
			break;
		err = 0;
		break;
	case HARMONY_PORT_OUTPUT_GAIN:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		cp->un.ord = sc->sc_outputgain ? 1 : 0;
		err = 0;
		break;
	case HARMONY_PORT_MONITOR_LVL:
		if (cp->type != AUDIO_MIXER_VALUE)
			break;
		if (cp->un.value.num_channels != 1)
			break;
		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
		    sc->sc_monitor_lvl.left;
		err = 0;
		break;
	case HARMONY_PORT_RECORD_SOURCE:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		cp->un.ord = sc->sc_in_port;
		err = 0;
		break;
	case HARMONY_PORT_OUTPUT_SOURCE:
		if (cp->type != AUDIO_MIXER_ENUM)
			break;
		cp->un.ord = sc->sc_out_port;
		err = 0;
		break;
	}
	return (0);
}

int
harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip)
{
	int err = 0;

	switch (dip->index) {
	case HARMONY_PORT_INPUT_LVL:
		dip->type = AUDIO_MIXER_VALUE;
		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioNinput, sizeof dip->label.name);
		dip->un.v.num_channels = 2;
		strlcpy(dip->un.v.units.name, AudioNvolume,
		    sizeof dip->un.v.units.name);
		break;
	case HARMONY_PORT_INPUT_OV:
		dip->type = AUDIO_MIXER_ENUM;
		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, "overrange", sizeof dip->label.name);
		dip->un.e.num_mem = 2;
		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
		    sizeof dip->un.e.member[0].label.name);
		dip->un.e.member[0].ord = 0;
		strlcpy(dip->un.e.member[1].label.name, AudioNon,
		    sizeof dip->un.e.member[1].label.name);
		dip->un.e.member[1].ord = 1;
		break;
	case HARMONY_PORT_OUTPUT_LVL:
		dip->type = AUDIO_MIXER_VALUE;
		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
		dip->un.v.num_channels = 2;
		strlcpy(dip->un.v.units.name, AudioNvolume,
		    sizeof dip->un.v.units.name);
		break;
	case HARMONY_PORT_OUTPUT_GAIN:
		dip->type = AUDIO_MIXER_ENUM;
		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, "gain", sizeof dip->label.name);
		dip->un.e.num_mem = 2;
		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
		    sizeof dip->un.e.member[0].label.name);
		dip->un.e.member[0].ord = 0;
		strlcpy(dip->un.e.member[1].label.name, AudioNon,
		    sizeof dip->un.e.member[1].label.name);
		dip->un.e.member[1].ord = 1;
		break;
	case HARMONY_PORT_MONITOR_LVL:
		dip->type = AUDIO_MIXER_VALUE;
		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
		dip->un.v.num_channels = 1;
		strlcpy(dip->un.v.units.name, AudioNvolume,
		    sizeof dip->un.v.units.name);
		break;
	case HARMONY_PORT_RECORD_SOURCE:
		dip->type = AUDIO_MIXER_ENUM;
		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
		dip->un.e.num_mem = 2;
		strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
		    sizeof dip->un.e.member[0].label.name);
		dip->un.e.member[0].ord = HARMONY_IN_MIC;
		strlcpy(dip->un.e.member[1].label.name, AudioNline,
		    sizeof dip->un.e.member[1].label.name);
		dip->un.e.member[1].ord = HARMONY_IN_LINE;
		break;
	case HARMONY_PORT_OUTPUT_SOURCE:
		dip->type = AUDIO_MIXER_ENUM;
		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
		dip->un.e.num_mem = 3;
		strlcpy(dip->un.e.member[0].label.name, AudioNline,
		    sizeof dip->un.e.member[0].label.name);
		dip->un.e.member[0].ord = HARMONY_OUT_LINE;
		strlcpy(dip->un.e.member[1].label.name, AudioNspeaker,
		    sizeof dip->un.e.member[1].label.name);
		dip->un.e.member[1].ord = HARMONY_OUT_SPEAKER;
		strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
		    sizeof dip->un.e.member[2].label.name);
		dip->un.e.member[2].ord = HARMONY_OUT_HEADPHONE;
		break;
	case HARMONY_PORT_INPUT_CLASS:
		dip->type = AUDIO_MIXER_CLASS;
		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
		break;
	case HARMONY_PORT_OUTPUT_CLASS:
		dip->type = AUDIO_MIXER_CLASS;
		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
		break;
	case HARMONY_PORT_MONITOR_CLASS:
		dip->type = AUDIO_MIXER_CLASS;
		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
		break;
	case HARMONY_PORT_RECORD_CLASS:
		dip->type = AUDIO_MIXER_CLASS;
		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
		dip->prev = dip->next = AUDIO_MIXER_LAST;
		strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
		break;
	default:
		err = ENXIO;
		break;
	}

	return (err);
}

void *
harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags)
{
	struct harmony_softc *sc = vsc;
	struct harmony_dma *d;
	int rseg;

	d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags);
	if (d == NULL)
		goto fail;

	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT,
	    &d->d_map) != 0)
		goto fail1;

	if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1,
	    &rseg, BUS_DMA_NOWAIT) != 0)
		goto fail2;

	if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva,
	    BUS_DMA_NOWAIT) != 0)
		goto fail3;

	if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL,
	    BUS_DMA_NOWAIT) != 0)
		goto fail4;

	d->d_next = sc->sc_dmas;
	sc->sc_dmas = d;
	d->d_size = size;
	return (d->d_kva);

fail4:
	bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size);
fail3:
	bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
fail2:
	bus_dmamap_destroy(sc->sc_dmat, d->d_map);
fail1:
	free(d, pool, 0);
fail:
	return (NULL);
}

void
harmony_freem(void *vsc, void *ptr, int pool)
{
	struct harmony_softc *sc = vsc;
	struct harmony_dma *d, **dd;

	for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) {
		if (d->d_kva != ptr)
			continue;
		bus_dmamap_unload(sc->sc_dmat, d->d_map);
		bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size);
		bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
		bus_dmamap_destroy(sc->sc_dmat, d->d_map);
		free(d, pool, 0);
		return;
	}
	printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname);
}

size_t
harmony_round_buffersize(void *vsc, int direction, size_t size)
{
	return ((size + HARMONY_BUFSIZE - 1) & (size_t)(-HARMONY_BUFSIZE));
}

int
harmony_get_props(void *vsc)
{
	return (AUDIO_PROP_FULLDUPLEX);
}

int
harmony_trigger_output(void *vsc, void *start, void *end, int blksize,
    void (*intr)(void *), void *intrarg, struct audio_params *param)
{
	struct harmony_softc *sc = vsc;
	struct harmony_channel *c = &sc->sc_playback;
	struct harmony_dma *d;
	bus_addr_t nextaddr;
	bus_size_t togo;

	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
		/*EMPTY*/;
	if (d == NULL) {
		printf("%s: trigger_output: bad addr: %p\n",
		    sc->sc_dv.dv_xname, start);
		return (EINVAL);
	}

	c->c_intr = intr;
	c->c_intrarg = intrarg;
	c->c_blksz = blksize;
	c->c_current = d;
	c->c_segsz = (caddr_t)end - (caddr_t)start;
	c->c_cnt = 0;
	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;

	sc->sc_playing = 1;

	togo = c->c_segsz - c->c_cnt;
	if (togo == 0) {
		nextaddr = d->d_map->dm_segs[0].ds_addr;
		c->c_cnt = togo = c->c_blksz;
	} else {
		nextaddr = c->c_lastaddr;
		if (togo > c->c_blksz)
			togo = c->c_blksz;
		c->c_cnt += togo;
	}

	bus_dmamap_sync(sc->sc_dmat, d->d_map,
	    nextaddr - d->d_map->dm_segs[0].ds_addr,
	    c->c_blksz, BUS_DMASYNC_PREWRITE);

	mtx_enter(&audio_lock);
	WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
	c->c_theaddr = nextaddr;
	SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
	c->c_lastaddr = nextaddr + togo;

	harmony_start_cp(sc);
	harmony_intr_enable(sc);
	mtx_leave(&audio_lock);
	return (0);
}

void
harmony_start_cp(struct harmony_softc *sc)
{
	struct harmony_channel *c = &sc->sc_capture;
	struct harmony_dma *d;
	bus_addr_t nextaddr;
	bus_size_t togo;

	if (sc->sc_capturing == 0) {
		WRITE_REG(sc, HARMONY_RNXTADD,
		    sc->sc_capture_paddrs[sc->sc_capture_empty]);
		if (++sc->sc_capture_empty == CAPTURE_EMPTYS)
			sc->sc_capture_empty = 0;
	} else {
		d = c->c_current;
		togo = c->c_segsz - c->c_cnt;
		if (togo == 0) {
			nextaddr = d->d_map->dm_segs[0].ds_addr;
			c->c_cnt = togo = c->c_blksz;
		} else {
			nextaddr = c->c_lastaddr;
			if (togo > c->c_blksz)
				togo = c->c_blksz;
			c->c_cnt += togo;
		}

		bus_dmamap_sync(sc->sc_dmat, d->d_map,
		    nextaddr - d->d_map->dm_segs[0].ds_addr,
		    c->c_blksz, BUS_DMASYNC_PREWRITE);

		WRITE_REG(sc, HARMONY_RNXTADD, nextaddr);
		SYNC_REG(sc, HARMONY_RNXTADD, BUS_SPACE_BARRIER_WRITE);
		c->c_lastaddr = nextaddr + togo;
	}

	timeout_add(&sc->sc_acc_tmo, 1);
}

int
harmony_trigger_input(void *vsc, void *start, void *end, int blksize,
    void (*intr)(void *), void *intrarg, struct audio_params *param)
{
	struct harmony_softc *sc = vsc;
	struct harmony_channel *c = &sc->sc_capture;
	struct harmony_dma *d;

	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
		/*EMPTY*/;
	if (d == NULL) {
		printf("%s: trigger_input: bad addr: %p\n",
		    sc->sc_dv.dv_xname, start);
		return (EINVAL);
	}

	c->c_intr = intr;
	c->c_intrarg = intrarg;
	c->c_blksz = blksize;
	c->c_current = d;
	c->c_segsz = (caddr_t)end - (caddr_t)start;
	c->c_cnt = 0;
	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
	mtx_enter(&audio_lock);
	sc->sc_capturing = 1;
	harmony_start_cp(sc);
	harmony_intr_enable(sc);
	mtx_leave(&audio_lock);
	return (0);
}

static const struct speed_struct {
	u_int32_t speed;
	u_int32_t bits;
} harmony_speeds[] = {
	{ 5125, CNTL_RATE_5125 },
	{ 6615, CNTL_RATE_6615 },
	{ 8000, CNTL_RATE_8000 },
	{ 9600, CNTL_RATE_9600 },
	{ 11025, CNTL_RATE_11025 },
	{ 16000, CNTL_RATE_16000 },
	{ 18900, CNTL_RATE_18900 },
	{ 22050, CNTL_RATE_22050 },
	{ 27428, CNTL_RATE_27428 },
	{ 32000, CNTL_RATE_32000 },
	{ 33075, CNTL_RATE_33075 },
	{ 37800, CNTL_RATE_37800 },
	{ 44100, CNTL_RATE_44100 },
	{ 48000, CNTL_RATE_48000 },
};

u_int32_t
harmony_speed_bits(struct harmony_softc *sc, u_long *speedp)
{
	int i, n, selected = -1;

	n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]);

	if ((*speedp) <= harmony_speeds[0].speed)
		selected = 0;
	else if ((*speedp) >= harmony_speeds[n - 1].speed)
		selected = n - 1;
	else {
		for (i = 1; selected == -1 && i < n; i++) {
			if ((*speedp) == harmony_speeds[i].speed)
				selected = i;
			else if ((*speedp) < harmony_speeds[i].speed) {
				int diff1, diff2;

				diff1 = (*speedp) - harmony_speeds[i - 1].speed;
				diff2 = harmony_speeds[i].speed - (*speedp);
				if (diff1 < diff2)
					selected = i - 1;
				else
					selected = i;
			}
		}
	}

	if (selected == -1)
		selected = 2;

	*speedp = harmony_speeds[selected].speed;
	return (harmony_speeds[selected].bits);
}

int
harmony_set_gainctl(struct harmony_softc *sc)
{
	u_int32_t bits, mask, val, old;

	/* XXX leave these bits alone or the chip will not come out of CNTL */
	bits = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;

	/* input level */
	bits |= ((sc->sc_input_lvl.left >> (8 - GAINCTL_INPUT_BITS)) <<
	    GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M;
	bits |= ((sc->sc_input_lvl.right >> (8 - GAINCTL_INPUT_BITS)) <<
	    GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M;

	/* output level (inverted) */
	mask = (1 << GAINCTL_OUTPUT_BITS) - 1;
	val = mask - (sc->sc_output_lvl.left >> (8 - GAINCTL_OUTPUT_BITS));
	bits |= (val << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M;
	val = mask - (sc->sc_output_lvl.right >> (8 - GAINCTL_OUTPUT_BITS));
	bits |= (val << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M;

	/* monitor level (inverted) */
	mask = (1 << GAINCTL_MONITOR_BITS) - 1;
	val = mask - (sc->sc_monitor_lvl.left >> (8 - GAINCTL_MONITOR_BITS));
	bits |= (val << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M;

	/* XXX messing with these causes CNTL_C to get stuck... grr. */
	bits &= ~GAINCTL_IS_MASK;
	if (sc->sc_in_port == HARMONY_IN_MIC)
		bits |= GAINCTL_IS_LINE;
	else
		bits |= GAINCTL_IS_MICROPHONE;

	/* XXX messing with these causes CNTL_C to get stuck... grr. */
	bits &= ~(GAINCTL_LE | GAINCTL_HE | GAINCTL_SE);
	if (sc->sc_out_port == HARMONY_OUT_LINE)
		bits |= GAINCTL_LE;
	else if (sc->sc_out_port == HARMONY_OUT_SPEAKER)
		bits |= GAINCTL_SE;
	else
		bits |= GAINCTL_HE;

	mask = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
	old = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL);
	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, bits);
	if ((old & mask) != (bits & mask))
		return (1);
	return (0);
}

void
harmony_try_more(struct harmony_softc *sc)
{
	struct harmony_channel *c = &sc->sc_playback;
	struct harmony_dma *d = c->c_current;
	u_int32_t cur;
	int i, nsegs;

	cur = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_PCURADD);
	cur &= PCURADD_BUFMASK;
	nsegs = 0;

#ifdef DIAGNOSTIC
	if (cur < d->d_map->dm_segs[0].ds_addr ||
	    cur >= (d->d_map->dm_segs[0].ds_addr + c->c_segsz))
		panic("%s: bad current %x < %lx || %x > %lx",
		    sc->sc_dv.dv_xname, cur, d->d_map->dm_segs[0].ds_addr, cur,
		    d->d_map->dm_segs[0].ds_addr + c->c_segsz);
#endif /* DIAGNOSTIC */

	if (cur > c->c_theaddr) {
		nsegs = (cur - c->c_theaddr) / HARMONY_BUFSIZE;
	} else if (cur < c->c_theaddr) {
		nsegs = (d->d_map->dm_segs[0].ds_addr + c->c_segsz -
		    c->c_theaddr) / HARMONY_BUFSIZE;
		nsegs += (cur - d->d_map->dm_segs[0].ds_addr) /
		    HARMONY_BUFSIZE;
	}

	if (nsegs != 0 && c->c_intr != NULL) {
		for (i = 0; i < nsegs; i++)
			(*c->c_intr)(c->c_intrarg);
		c->c_theaddr = cur;
	}
}

struct cfdriver harmony_cd = {
	NULL, "harmony", DV_DULL
};

struct cfattach harmony_ca = {
	sizeof(struct harmony_softc), harmony_match, harmony_attach
};


code:
/*	$OpenBSD: harmonyreg.h,v 1.5 2003/06/02 19:54:29 jason Exp $	*/

/*
 * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
 * 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 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.
 */

/*
 * Harmony CS4215/AD1849 register definitions based on:
 *  "712 I/O Subsystem ERS", Revision 1.1, 12 February 1993
 */

/* harmony always uses a 4K buffer */
#define	HARMONY_BUFSIZE			4096

#define	HARMONY_NREGS	0x40

#define	HARMONY_ID		0x00		/* identification */
#define	HARMONY_RESET		0x04		/* reset */
#define	HARMONY_CNTL		0x08		/* control */
#define	HARMONY_GAINCTL		0x0c		/* gain control */
#define	HARMONY_PNXTADD		0x10		/* play next address */
#define	HARMONY_PCURADD		0x14		/* play current address */
#define	HARMONY_RNXTADD		0x18		/* record next address */
#define	HARMONY_RCURADD		0x1c		/* record current address */
#define	HARMONY_DSTATUS		0x20		/* device status */
#define	HARMONY_OV		0x24		/* overrange input */
#define	HARMONY_PIO		0x28		/* general purpose i/o */
#define	HARMONY_DIAG		0x3c		/* chi diagnostic */

/* HARMONY_ID */
#define	ID_REV_MASK		0x00ff0000	/* revision mask: */
#define	ID_REV_SHIFT		16
#define	ID_REV_TS		0x00150000	/*  teleshare installed */
#define	ID_REV_NOTS		0x00140000	/*  teleshare not installed */
#define	ID_CHIID		0x0000f000	/* CHI identification */
#define	ID_CHIID_SHIFT		12

/* HARMONY_RESET */
#define	RESET_RST		0x00000001	/* reset codec */

/* HARMONY_CNTL */
#define	CNTL_C			0x80000000	/* control mode */
#define	CNTL_CODEC_REV_MASK	0x0ff00000	/* codec revision */
#define	CNTL_CODEC_REV_SHIFT	20
#define	CNTL_EXP_3		0x00020000	/* expansion bit 3 */
#define	CNTL_EXP_2		0x00010000	/* expansion bit 2 */
#define	CNTL_EXP_1		0x00008000	/* expansion bit 1 */
#define	CNTL_EXP_0		0x00004000	/* expansion bit 0 */
#define	CNTL_AC			0x00002000	/* autocalibration ad1849 */
#define	CNTL_AD			0x00001000	/* ad1849 compat? */
#define	CNTL_OLB		0x00000800	/* output level */
#define	CNTL_ITS		0x00000400	/* codec immediate tristate */
#define	CNTL_LS_MASK		0x00000300	/* loopback select: */
#define	CNTL_LS_NONE		0x00000000	/*  none */
#define	CNTL_LS_INTERNAL	0x00000100	/*  internal */
#define	CNTL_LS_DIGITAL		0x00000200	/*  digital */
#define	CNTL_LS_ANALOG		0x00000300	/*  analog */
#define	CNTL_FORMAT_MASK	0x000000c0	/* encoding format: */
#define	CNTL_FORMAT_SLINEAR16BE	0x00000000	/*  16 bit signed linear be */
#define	CNTL_FORMAT_ULAW	0x00000040	/*  8 bit ulaw */
#define	CNTL_FORMAT_ALAW	0x00000080	/*  8 bit alaw */
#define	CNTL_FORMAT_ULINEAR8	0x000000c0	/*  8 bit unsigned linear */
#define	CNTL_CHANS_MASK		0x00000020	/* number of channels: */
#define	CNTL_CHANS_MONO		0x00000000	/*  mono */
#define	CNTL_CHANS_STEREO	0x00000020	/*  stereo */
#define	CNTL_RATE_MASK		0x0000001f	/* sample rate (kHz): */
#define	CNTL_RATE_5125		0x00000010	/*  5.5125 */
#define	CNTL_RATE_6615		0x00000017	/*  6.615 */
#define	CNTL_RATE_8000		0x00000008	/*  8 */
#define	CNTL_RATE_9600		0x0000000f	/*  9.6 */
#define	CNTL_RATE_11025		0x00000011	/*  11.025 */
#define	CNTL_RATE_16000		0x00000009	/*  16 */
#define	CNTL_RATE_18900		0x00000012	/*  18.9 */
#define	CNTL_RATE_22050		0x00000013	/*  22.05 */
#define	CNTL_RATE_27428		0x0000000a	/*  27.42857 */
#define	CNTL_RATE_32000		0x0000000b	/*  32 */
#define	CNTL_RATE_33075		0x00000016	/*  33.075 */
#define	CNTL_RATE_37800		0x00000014	/*  37.8 */
#define	CNTL_RATE_44100		0x00000015	/*  44.1 */
#define	CNTL_RATE_48000		0x0000000e	/*  48 */

/* HARMONY_GAINCTL */
#define	GAINCTL_HE		0x08000000	/* headphones enable */
#define	GAINCTL_LE		0x04000000	/* line output enable */
#define	GAINCTL_SE		0x02000000	/* speaker enable */
#define	GAINCTL_IS_MASK		0x01000000	/* input select: */
#define	GAINCTL_IS_LINE		0x00000000	/*  line input */
#define	GAINCTL_IS_MICROPHONE	0x01000000	/*  microphone */
#define	GAINCTL_INPUT_LEFT_M	0x0000f000	/* left input gain */
#define	GAINCTL_INPUT_LEFT_S	12
#define	GAINCTL_INPUT_RIGHT_M	0x000f0000	/* left input gain */
#define	GAINCTL_INPUT_RIGHT_S	16
#define	GAINCTL_INPUT_BITS	4
#define	GAINCTL_MONITOR_M	0x00f00000	/* monitor gain (inverted) */
#define	GAINCTL_MONITOR_S	20
#define	GAINCTL_MONITOR_BITS	4
#define	GAINCTL_OUTPUT_LEFT_M	0x00000fc0	/* left out gain (inverted) */
#define	GAINCTL_OUTPUT_LEFT_S	6
#define	GAINCTL_OUTPUT_RIGHT_M	0x0000003f	/* right out gain (inverted) */
#define	GAINCTL_OUTPUT_RIGHT_S	0
#define	GAINCTL_OUTPUT_BITS	6

/* HARMONY_PCURADD */
#define	PCURADD_BUFMASK		(~(HARMONY_BUFSIZE - 1))

/* HARMONY_RCURADD */
#define	PCURADD_BUFMASK		(~(HARMONY_BUFSIZE - 1))

/* HARMONY_DSTATUS */
#define	DSTATUS_IE		0x80000000	/* interrupt enable */
#define	DSTATUS_PN		0x00000200	/* playback next empty */
#define	DSTATUS_PC		0x00000100	/* playback dma active */
#define	DSTATUS_RN		0x00000002	/* record next empty */
#define	DSTATUS_RC		0x00000001	/* record dma active */

/* HARMONY_OV */
#define	OV_OV			0x00000001	/* input over range */

/* HARMONY_PIO */
#define	PIO_PO			0x00000002	/* parallel output */
#define	PIO_PI			0x00000001	/* parallel input */

/* HARMONY_DIAG */
#define	DIAG_CO			0x00000001	/* sclk from codec */

/* CS4215_REV */
#define	CS4215_REV_VER		0x0f
#define	CS4215_REV_VER_C	0x00		/* CS4215 rev C */
#define	CS4215_REV_VER_D	0x01		/* CS4215 rev D */
#define	CS4215_REV_VER_E	0x02		/* CS4215 rev E/AD1849K */


code:
/*	$OpenBSD: harmonyvar.h,v 1.8 2003/08/15 13:25:53 mickey Exp $	*/

/*
 * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
 * 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 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.
 */

#define	HARMONY_PORT_INPUT_LVL		0
#define	HARMONY_PORT_INPUT_OV		1
#define	HARMONY_PORT_OUTPUT_LVL		2
#define	HARMONY_PORT_OUTPUT_GAIN	3
#define	HARMONY_PORT_MONITOR_LVL	4
#define	HARMONY_PORT_RECORD_SOURCE	5
#define	HARMONY_PORT_OUTPUT_SOURCE	6
#define	HARMONY_PORT_INPUT_CLASS	7
#define	HARMONY_PORT_OUTPUT_CLASS	8
#define	HARMONY_PORT_MONITOR_CLASS	9
#define	HARMONY_PORT_RECORD_CLASS	10

#define	HARMONY_IN_MIC			0
#define	HARMONY_IN_LINE			1

#define	HARMONY_OUT_LINE		0
#define	HARMONY_OUT_SPEAKER		1
#define	HARMONY_OUT_HEADPHONE		2

#define	PLAYBACK_EMPTYS			3	/* playback empty buffers */
#define	CAPTURE_EMPTYS			3	/* capture empty buffers */

struct harmony_volume {
	u_char left, right;
};

struct harmony_empty {
	u_int8_t	playback[PLAYBACK_EMPTYS][HARMONY_BUFSIZE];
	u_int8_t	capture[CAPTURE_EMPTYS][HARMONY_BUFSIZE];
};

struct harmony_dma {
	struct harmony_dma *d_next;
	bus_dmamap_t d_map;
	bus_dma_segment_t d_seg;
	caddr_t d_kva;
	size_t d_size;
};

struct harmony_channel {
	struct harmony_dma *c_current;
	bus_size_t c_segsz;
	bus_size_t c_cnt;
	bus_size_t c_blksz;
	bus_addr_t c_lastaddr;
	void (*c_intr)(void *);
	void *c_intrarg;
	bus_addr_t c_theaddr;
};

struct harmony_softc {
	struct device sc_dv;
	struct audio_device sc_audev;

	bus_dma_tag_t sc_dmat;
	bus_space_tag_t sc_bt;
	bus_space_handle_t sc_bh;
	int sc_open;
	u_int32_t sc_cntlbits;
	int sc_need_commit;
	int sc_playback_empty;
	bus_addr_t sc_playback_paddrs[PLAYBACK_EMPTYS];
	int sc_capture_empty;
	bus_addr_t sc_capture_paddrs[CAPTURE_EMPTYS];
	bus_dmamap_t sc_empty_map;
	bus_dma_segment_t sc_empty_seg;
	int sc_empty_rseg;
	struct harmony_empty *sc_empty_kva;
	struct harmony_dma *sc_dmas;
	int sc_playing, sc_capturing;
	struct harmony_channel sc_playback, sc_capture;
	struct harmony_volume sc_monitor_lvl, sc_input_lvl, sc_output_lvl;
	int sc_in_port, sc_out_port, sc_hasulinear8;
	int sc_micpreamp, sc_ov, sc_outputgain;
	int sc_teleshare;

	struct timeout sc_acc_tmo;
	u_int32_t sc_acc, sc_acc_num, sc_acc_cnt;
};

#define	READ_REG(sc, reg)		\
    bus_space_read_4((sc)->sc_bt, (sc)->sc_bh, (reg))
#define	WRITE_REG(sc, reg, val)		\
    bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, (reg), (val))
#define	SYNC_REG(sc, reg, flags)	\
    bus_space_barrier((sc)->sc_bt, (sc)->sc_bh, (reg), sizeof(u_int32_t), \
	(flags))


code:
/*	$OpenBSD: hil_gsc.c,v 1.5 2005/12/22 07:09:52 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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 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/device.h>

#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <machine/bus.h>

#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>

#include <machine/hil_machdep.h>

#include <dev/hil/hilvar.h>

int	hil_gsc_match(struct device *, void *, void *);
void	hil_gsc_attach(struct device *, struct device *, void *);

struct hil_gsc_softc {
	struct hil_softc sc_hs;
	int		 sc_hil_console;
};

struct cfattach hil_gsc_ca = {
	sizeof(struct hil_gsc_softc), hil_gsc_match, hil_gsc_attach
};

int
hil_gsc_match(struct device *parent, void *match, void *aux)
{
	struct gsc_attach_args *ga = aux;

	if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
	    ga->ga_type.iodc_sv_model != HPPA_FIO_HIL)
		return (0);

	return (1);
}

void
hil_gsc_attach(struct device *parent, struct device *self, void *aux)
{
	struct hil_gsc_softc *gsc = (void *)self;
	struct hil_softc *sc = &gsc->sc_hs;
	struct gsc_attach_args *ga = aux;

	sc->sc_bst = ga->ga_iot;
	if (bus_space_map(ga->ga_iot, ga->ga_hpa,
	    HILMAPSIZE, 0, &sc->sc_bsh)) {
		printf(": couldn't map hil controller\n");
		return;
	}

	gsc->sc_hil_console = ga->ga_dp.dp_mod == PAGE0->mem_kbd.pz_dp.dp_mod &&
	    bcmp(ga->ga_dp.dp_bc, PAGE0->mem_kbd.pz_dp.dp_bc, 6) == 0;

	hil_attach(sc, &gsc->sc_hil_console);

	gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq, IPL_TTY,
	    hil_intr, sc, sc->sc_dev.dv_xname);

	startuphook_establish(hil_attach_deferred, sc);
}


code:
/*	$OpenBSD: if_ie_gsc.c,v 1.26 2014/03/29 18:09:29 guenther Exp $	*/

/*
 * Copyright (c) 1998-2004 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.
 */

/*
 * References:
 * 1. 82596DX and 82596SX High-Performance 32-bit Local Area Network Coprocessor
 *    Intel Corporation, November 1996, Order Number: 290219-006
 *
 * 2. 712 I/O Subsystem ERS Rev 1.0
 *    Hewlett-Packard, June 17 1992, Dwg No. A-A2263-66510-31
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/sockio.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_media.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

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

#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>

#include <dev/ic/i82596reg.h>
#include <dev/ic/i82596var.h>

#define	IEGSC_GECKO	IEMD_FLAG0

struct ie_gsc_regs {
	u_int32_t	ie_reset;
	u_int32_t	ie_port;
	u_int32_t	ie_attn;
};

#define	IE_SIZE	0x8000

int	ie_gsc_probe(struct device *, void *, void *);
void	ie_gsc_attach(struct device *, struct device *, void *);

struct cfattach ie_gsc_ca = {
	sizeof(struct ie_softc), ie_gsc_probe, ie_gsc_attach
};

static int ie_gsc_media[] = {
	IFM_ETHER | IFM_10_2,
};
#define	IE_NMEDIA	(sizeof(ie_gsc_media) / sizeof(ie_gsc_media[0]))

char *ie_mem;

void ie_gsc_reset(struct ie_softc *sc, int what);
void ie_gsc_attend(struct ie_softc *sc);
void ie_gsc_run(struct ie_softc *sc);
void ie_gsc_port(struct ie_softc *sc, u_int);
#ifdef USELEDS
int ie_gsc_intrhook(struct ie_softc *sc, int what);
#endif
u_int16_t ie_gsc_read16(struct ie_softc *sc, int offset);
void ie_gsc_write16(struct ie_softc *sc, int offset, u_int16_t v);
void ie_gsc_write24(struct ie_softc *sc, int offset, int addr);
void ie_gsc_memcopyin(struct ie_softc *sc, void *p, int offset, size_t);
void ie_gsc_memcopyout(struct ie_softc *sc, const void *p, int, size_t);

void
ie_gsc_reset(sc, what)
	struct ie_softc *sc;
	int what;
{
	volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
	int i;

	r->ie_reset = 0;
	/*
	 * per [2] 4.6.2.1
	 * delay for 10 system clocks + 5 transmit clocks,
	 * NB: works for system clocks over 10MHz
	 */
	DELAY(1000);

	switch (what) {
	case IE_CHIP_PROBE:
		break;

	case IE_CARD_RESET:
		/*
		 * after the hardware reset:
		 * inform i825[89]6 about new SCP address,
		 * maddr must be at least 16-byte aligned
		 */
		ie_gsc_port(sc, IE_PORT_SCP);
		ie_gsc_attend(sc);

		for (i = 9000; i-- && ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp));
		     DELAY(100))
			pdcache(0, sc->sc_maddr + sc->iscp, IE_ISCP_SZ);

#ifdef I82596_DEBUG
		if (i < 0) {
			printf("timeout for PORT command (%x)%s\n",
			       ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp)),
			       (sc->sc_flags & IEGSC_GECKO)? " on gecko":"");
			return;
		}
#endif
		break;
	}
}

void
ie_gsc_attend(sc)
	struct ie_softc *sc;
{
	volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;

	fdcache(0, (vaddr_t)ie_mem, IE_SIZE);
	DELAY(1);
	r->ie_attn = 0;
	DELAY(1);
}

void
ie_gsc_run(sc)
	struct ie_softc *sc;
{
}

void
ie_gsc_port(sc, cmd)
	struct ie_softc *sc;
	u_int cmd;
{
	switch (cmd) {
	case IE_PORT_RESET:
		cmd = 0;
		break;
	case IE_PORT_TEST:
		cmd = ((u_int)sc->sc_maddr + sc->scp) | 1;
		break;
	case IE_PORT_SCP:
		cmd = ((u_int)sc->sc_maddr + sc->scp) | 2;
		break;
	case IE_PORT_DUMP:
		cmd = 3;
		break;
	}

	if (sc->sc_flags & IEGSC_GECKO) {
		volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
		r->ie_port = cmd & 0xffff;
		DELAY(1000);
		r->ie_port = cmd >> 16;
		DELAY(1000);
	} else {
		volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
		r->ie_port = cmd >> 16;
		DELAY(1000);
		r->ie_port = cmd & 0xffff;
		DELAY(1000);
	}
}

#ifdef USELEDS
int
ie_gsc_intrhook(sc, where)
	struct ie_softc *sc;
	int where;
{
	switch (where) {
	case IE_INTR_ENRCV:
		ledctl(PALED_NETRCV, 0, 0);
		break;
	case IE_INTR_ENSND:
		ledctl(PALED_NETSND, 0, 0);
		break;
	case IE_INTR_EXIT:
	case IE_INTR_LOOP:
		fdcache(0, (vaddr_t)ie_mem, IE_SIZE);
		break;
	}
	return 0;
}
#endif

u_int16_t
ie_gsc_read16(sc, offset)
	struct ie_softc *sc;
	int offset;
{
	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);

	asm volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr));
	return *addr;
}

void
ie_gsc_write16(sc, offset, v)
	struct ie_softc *sc;
	int offset;
	u_int16_t v;
{
	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);

	*addr = v;
	asm volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr));
}

void
ie_gsc_write24(sc, offset, v)
	struct ie_softc *sc;
	int offset;
	int v;
{
	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);

	addr[0] = (v      ) & 0xffff;
	addr[1] = (v >> 16) & 0xffff;
	asm volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr+0));
	asm volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr+1));
}

void
ie_gsc_memcopyin(sc, p, offset, size)
	struct ie_softc	*sc;
	void *p;
	int offset;
	size_t size;
{
	pdcache(0, sc->bh + offset, size);
	bcopy ((void *)((u_long)sc->bh + offset), p, size);
}

void
ie_gsc_memcopyout(sc, p, offset, size)
	struct ie_softc	*sc;
	const void *p;
	int offset;
	size_t size;
{
	bcopy (p, (void *)((u_long)sc->bh + offset), size);
	fdcache(0, sc->bh + offset, size);
}

int
ie_gsc_probe(parent, match, aux)
	struct device *parent;
	void *match, *aux;
{
	struct gsc_attach_args *ga = aux;

	if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
	    (ga->ga_type.iodc_sv_model != HPPA_FIO_LAN &&
	     ga->ga_type.iodc_sv_model != HPPA_FIO_GLAN))
		return 0;

	return 1;
}

void
ie_gsc_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct pdc_lan_station_id pdc_mac PDC_ALIGNMENT;
	struct ie_softc *sc = (struct ie_softc *)self;
	struct gsc_attach_args *ga = aux;
	/*bus_dma_segment_t seg;
	int rseg;*/
	int rv;
#ifdef PMAPDEBUG
	extern int pmapdebug;
	int opmapdebug = pmapdebug;
	pmapdebug = 0;
#endif

	sc->iot = sc->bt = ga->ga_iot;
	if (bus_space_map(sc->iot, ga->ga_hpa, IOMOD_HPASIZE, 0, &sc->ioh)) {
		printf(": can't map IO space\n");
		return;
	}

	if (ga->ga_type.iodc_sv_model == HPPA_FIO_GLAN)
		sc->sc_flags |= IEGSC_GECKO;

	sc->sc_msize = IE_SIZE;
	/* XXX memory must be under 16M until the mi part is fixed */
#if 0
	if (bus_dmamem_alloc(ga->ga_dmatag, sc->sc_msize, NBPG, 0,
			     &seg, 1, &rseg, BUS_DMA_NOWAIT)) {
		printf (": cannot allocate %d bytes of DMA memory\n",
			sc->sc_msize);
		return;
	}
	if (bus_dmamem_map(ga->ga_dmatag, &seg, rseg, sc->sc_msize,
			   (caddr_t *)&sc->bh, BUS_DMA_NOWAIT)) {
		printf (": cannot map DMA memory\n");
		bus_dmamem_free(ga->ga_dmatag, &seg, rseg);
		return;
	}

	bzero((void *)sc->bh, sc->sc_msize);
	sc->sc_maddr = kvtop((caddr_t)sc->bh);

#else
	sc->bh = (u_int)ie_mem;
	sc->sc_maddr = sc->bh;
#endif
	sc->sysbus = 0x40 | IE_SYSBUS_82586 | IE_SYSBUS_INTLOW | IE_SYSBUS_TRG | IE_SYSBUS_BE;

	sc->do_xmitnopchain = 0;
	sc->hwreset = ie_gsc_reset;
	sc->chan_attn = ie_gsc_attend;
	sc->port = ie_gsc_port;
	sc->hwinit = ie_gsc_run;
	sc->memcopyout = ie_gsc_memcopyout;
	sc->memcopyin = ie_gsc_memcopyin;
	sc->ie_bus_read16 = ie_gsc_read16;
	sc->ie_bus_write16 = ie_gsc_write16;
	sc->ie_bus_write24 = ie_gsc_write24;
#ifdef USELEDS
	sc->intrhook = ie_gsc_intrhook;
#else
	sc->intrhook = NULL;
#endif

#ifdef I82596_DEBUG
	printf(" mem %x[%p]/%x", sc->bh, sc->sc_maddr, sc->sc_msize);
	sc->sc_debug = IED_ALL;
#endif
	rv = i82596_probe(sc);
	if (!rv) {
		/*bus_dmamem_free(ga->ga_dmatag, &seg, sc->sc_msize);*/
	}
#ifdef PMAPDEBUG
	pmapdebug = opmapdebug;
#endif
	if (!rv) {
		printf("\n");
		return;
	}

	if (pdc_call((iodcio_t)pdc, 0, PDC_LAN_STATION_ID,
		     PDC_LAN_STATION_ID_READ, &pdc_mac, ga->ga_hpa) < 0)
		bcopy((void *)ASP_PROM, sc->sc_arpcom.ac_enaddr,
		      ETHER_ADDR_LEN);
	else
		bcopy(pdc_mac.addr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);

	printf(":");

	sc->iscp = 0;
	sc->scp = 32;
	sc->scb = 94;
	sc->buf_area = 256;
	sc->buf_area_sz = sc->sc_msize - sc->buf_area;
	sc->sc_type = sc->sc_flags & IEGSC_GECKO? "LASI/i82596CA" : "i82596DX";
	sc->sc_vers = ga->ga_type.iodc_model * 10 + ga->ga_type.iodc_sv_rev;
	i82596_attach(sc, sc->sc_type, (char *)sc->sc_arpcom.ac_enaddr,
		      ie_gsc_media, IE_NMEDIA, ie_gsc_media[0]);

	sc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
	    ga->ga_irq, IPL_NET, i82596_intr, sc, sc->sc_dev.dv_xname);
}


code:
/*	$OpenBSD: lpt_gsc.c,v 1.12 2011/09/16 17:20:07 miod Exp $	*/

/*
 * Copyright (c) 1998 Michael Shalayeff
 * Copyright (c) 1993, 1994 Charles Hannum.
 * Copyright (c) 1990 William F. Jolitz, TeleMuse
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This software is a component of "386BSD" developed by
 *	William F. Jolitz, TeleMuse.
 * 4. Neither the name of the developer nor the name "386BSD"
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
 * NOT MAKE USE OF THIS WORK.
 *
 * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
 * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
 * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES
 * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
 * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
 * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
 * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
 * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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/device.h>

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

#include <dev/ic/lptreg.h>
#include <dev/ic/lptvar.h>

#include <hppa/dev/cpudevs.h>

#include <hppa/gsc/gscbusvar.h>

#define	LPTGSC_OFFSET	0x800

int	lpt_gsc_probe(struct device *, void *, void *);
void	lpt_gsc_attach(struct device *, struct device *, void *);

struct cfattach lpt_gsc_ca = {
	sizeof(struct lpt_softc), lpt_gsc_probe, lpt_gsc_attach
};

/*
 * Logic:
 *	1) You should be able to write to and read back the same value
 *	   to the data port.  Do an alternating zeros, alternating ones,
 *	   walking zero, and walking one test to check for stuck bits.
 *
 *	2) You should be able to write to and read back the same value
 *	   to the control port lower 5 bits, the upper 3 bits are reserved
 *	   per the IBM PC technical reference manuals and different boards
 *	   do different things with them.  Do an alternating zeros, alternating
 *	   ones, walking zero, and walking one test to check for stuck bits.
 *
 *	   Some printers drag the strobe line down when the are powered off
 * 	   so this bit has been masked out of the control port test.
 *
 *	   XXX Some printers may not like a fast pulse on init or strobe, I
 *	   don't know at this point, if that becomes a problem these bits
 *	   should be turned off in the mask byte for the control port test.
 *
 *	3) Set the data and control ports to a value of 0
 */
int
lpt_gsc_probe(parent, match, aux)
	struct device *parent;
	void *match, *aux;
{
	struct gsc_attach_args *ga = aux;
	bus_space_handle_t ioh;
	bus_addr_t base;
	u_int8_t mask, data;
	int i, rv;

	if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
	    ga->ga_type.iodc_sv_model != HPPA_FIO_CENT)
		return 0;

#ifdef DEBUG
#define	ABORT								\
	do {								\
		printf("lpt_gsc_probe: mask %x data %x failed\n", mask,	\
		    data);						\
		return 0;						\
	} while (0)
#else
#define	ABORT	do {							\
			bus_space_unmap(ga->ga_iot, ioh, LPT_NPORTS);	\
			return 0;					\
		} while (0)
#endif

	base = ga->ga_hpa + LPTGSC_OFFSET;
	if (bus_space_map(ga->ga_iot, base, LPT_NPORTS, 0, &ioh)) {
		printf("lpt_gsc_probe: cannot map io space\n");
		return 0;
	}

	rv = 0;
	mask = 0xff;

	data = 0x55;				/* Alternating zeros */
	if (!lpt_port_test(ga->ga_iot, ioh, base, lpt_data, data, mask))
		ABORT;

	data = 0xaa;				/* Alternating ones */
	if (!lpt_port_test(ga->ga_iot, ioh, base, lpt_data, data, mask))
		ABORT;

	for (i = 0; i < CHAR_BIT; i++) {	/* Walking zero */
		data = ~(1 << i);
		if (!lpt_port_test(ga->ga_iot, ioh, base, lpt_data, data, mask))
			ABORT;
	}

	for (i = 0; i < CHAR_BIT; i++) {	/* Walking one */
		data = (1 << i);
		if (!lpt_port_test(ga->ga_iot, ioh, base, lpt_data, data, mask))
			ABORT;
	}

	bus_space_write_1(ga->ga_iot, ioh, lpt_data, 0);
	bus_space_write_1(ga->ga_iot, ioh, lpt_control, 0);
	bus_space_unmap(ga->ga_iot, ioh, LPT_NPORTS);

	return 1;
}

void
lpt_gsc_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct lpt_softc *sc = (void *)self;
	struct gsc_attach_args *ga = aux;
	bus_addr_t base;

	/* sc->sc_flags |= LPT_POLLED; */

	sc->sc_state = 0;
	sc->sc_iot = ga->ga_iot;
	base = ga->ga_hpa + LPTGSC_OFFSET;
	if (bus_space_map(ga->ga_iot, base, LPT_NPORTS, 0, &sc->sc_ioh)) {
		printf(": cannot map io space\n");
		return;
	}

	lpt_attach_common(sc);

	sc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
	    ga->ga_irq, IPL_TTY, lptintr, sc, sc->sc_dev.dv_xname);
}


code:
/*	$OpenBSD: mongoose_gsc.c,v 1.1 2004/10/30 10:26:32 miod Exp $	*/

/*
 * Copyright (c) 2004, Miodrag Vallat.
 *
 * 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 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/device.h>

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

#include <hppa/dev/cpudevs.h>

#include <dev/eisa/eisavar.h>
#include <dev/isa/isavar.h>

#include <hppa/dev/mongoosereg.h>
#include <hppa/dev/mongoosevar.h>

#include <hppa/gsc/gscbusvar.h>

void	mgattach_gsc(struct device *, struct device *, void *);
int	mgmatch_gsc(struct device *, void *, void *);

struct cfattach mg_gsc_ca = {
	sizeof(struct mongoose_softc), mgmatch_gsc, mgattach_gsc
};

int
mgmatch_gsc(struct device *parent, void *cfdata, void *aux)
{
	struct gsc_attach_args *ga = aux;

	if (ga->ga_type.iodc_type != HPPA_TYPE_BHA ||
	    ga->ga_type.iodc_sv_model != HPPA_BHA_WEISA)
		return (0);

	return (1);
}

void
mgattach_gsc(struct device *parent, struct device *self, void *aux)
{
	struct mongoose_softc *sc = (struct mongoose_softc *)self;
	struct gsc_attach_args *ga = aux;
	bus_space_handle_t ioh;

	sc->sc_bt = ga->ga_iot;
	sc->sc_iomap = ga->ga_hpa;

	if (bus_space_map(ga->ga_iot, ga->ga_hpa + MONGOOSE_MONGOOSE,
	    sizeof(struct mongoose_regs), 0, &ioh) != 0) {
		printf(": can't map IO space\n");
		return;
	}
	sc->sc_regs = (struct mongoose_regs *)ioh;

	if (bus_space_map(ga->ga_iot, ga->ga_hpa + MONGOOSE_CTRL,
	    sizeof(struct mongoose_ctrl), 0, &ioh) != 0) {
		printf(": can't map control registers\n");
		bus_space_unmap(ga->ga_iot, (bus_space_handle_t)sc->sc_regs,
		    sizeof(struct mongoose_regs));
		return;
	}
	sc->sc_ctrl = (struct mongoose_ctrl *)ioh;

	if (mgattach_common(sc) != 0)
		return;

	sc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
	    ga->ga_irq, IPL_HIGH, mg_intr, sc, sc->sc_dev.dv_xname);
}