code:
/*	$OpenBSD: macintr.c,v 1.52 2015/06/24 11:58:06 mpi Exp $	*/

/*-
 * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 1995 Per Fogelstrom
 * Copyright (c) 1993, 1994 Charles M. Hannum.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)isa.c	7.2 (Berkeley) 5/12/91
 */

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

#include <uvm/uvm_extern.h>
#include <ddb/db_var.h>

#include <machine/atomic.h>
#include <machine/autoconf.h>
#include <machine/intr.h>
#include <machine/psl.h>
#include <machine/pio.h>

#include <dev/ofw/openfirm.h>

#define ICU_LEN 64
#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))

int macintr_ienable_l[IPL_NUM], macintr_ienable_h[IPL_NUM];
int macintr_pri_share[IPL_NUM];

struct intrq macintr_handler[ICU_LEN];

void macintr_calc_mask(void);
void macintr_eoi(int irq);
int macintr_read_irq(void);

extern u_int32_t *heathrow_FCR;

#define INT_STATE_REG0  (interrupt_reg + 0x20)
#define INT_ENABLE_REG0 (interrupt_reg + 0x24)
#define INT_CLEAR_REG0  (interrupt_reg + 0x28)
#define INT_LEVEL_REG0  (interrupt_reg + 0x2c)
#define INT_STATE_REG1  (INT_STATE_REG0  - 0x10)
#define INT_ENABLE_REG1 (INT_ENABLE_REG0 - 0x10)
#define INT_CLEAR_REG1  (INT_CLEAR_REG0  - 0x10)
#define INT_LEVEL_REG1  (INT_LEVEL_REG0  - 0x10)

struct macintr_softc {
	struct device sc_dev;
};

int	macintr_match(struct device *parent, void *cf, void *aux);
void	macintr_attach(struct device *, struct device *, void *);
void	mac_ext_intr(void);
void	macintr_collect_preconf_intr(void);
void	macintr_setipl(int ipl);

struct cfattach macintr_ca = {
	sizeof(struct macintr_softc),
	macintr_match,
	macintr_attach
};

struct cfdriver macintr_cd = {
	NULL, "macintr", DV_DULL
};

int
macintr_match(struct device *parent, void *cf, void *aux)
{
	struct confargs *ca = aux;
	char type[40];

	/*
	 * Match entry according to "present" openfirmware entry.
	 */
	if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) {
		OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
		if (strcmp(type,  "interrupt-controller") == 0)
			return 1;
	}

	/*
	 * Check name for legacy interrupt controller, this is
	 * faked to allow old firmware which does not have an entry
	 * to attach to this device.
	 */
	if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 )
		return 1;
	return 0;
}

u_int8_t *interrupt_reg;
typedef void  (void_f) (void);
int macintr_prog_button (void *arg);

intr_establish_t macintr_establish;
intr_disestablish_t macintr_disestablish;

ppc_splraise_t macintr_splraise;
ppc_spllower_t macintr_spllower;
ppc_splx_t macintr_splx;

int
macintr_splraise(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int ocpl = ci->ci_cpl;
	int s;

	newcpl = macintr_pri_share[newcpl];
	if (ocpl > newcpl)
		newcpl = ocpl;

	s = ppc_intr_disable();
	macintr_setipl(newcpl);
	ppc_intr_enable(s);

	return ocpl;
}

int
macintr_spllower(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int ocpl = ci->ci_cpl;

	macintr_splx(newcpl);

	return ocpl;
}

void
macintr_splx(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int intr, s;

	intr = ppc_intr_disable();
	macintr_setipl(newcpl);
	if ((newcpl < IPL_SOFTTTY && ci->ci_ipending & ppc_smask[newcpl])) {
		s = splsofttty();
		dosoftint(newcpl);
		macintr_setipl(s); /* no-overhead splx */
	}
	ppc_intr_enable(intr);
}

void
macintr_attach(struct device *parent, struct device *self, void *aux)
{
	struct cpu_info *ci = curcpu();
	struct confargs *ca = aux;
	extern intr_establish_t *intr_establish_func;
	extern intr_disestablish_t *intr_disestablish_func;
	struct intrq *iq;
	int i;

	interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */

	for (i = 0; i < ICU_LEN; i++) {
		iq = &macintr_handler[i];
		TAILQ_INIT(&iq->iq_list);
	}
	ppc_smask_init();

	install_extint(mac_ext_intr);
	intr_establish_func  = macintr_establish;
	intr_disestablish_func  = macintr_disestablish;

	ppc_intr_func.raise = macintr_splraise;
	ppc_intr_func.lower = macintr_spllower;
	ppc_intr_func.x = macintr_splx;

	ci->ci_flags = 0;

	macintr_collect_preconf_intr();

	mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH,
	    macintr_prog_button, (void *)0x14, "progbutton");

	ppc_intr_enable(1);
	printf("\n");
}

void
macintr_collect_preconf_intr()
{
	int i;
	for (i = 0; i < ppc_configed_intr_cnt; i++) {
#ifdef DEBUG
		printf("\n\t%s irq %d level %d fun %p arg %p",
			ppc_configed_intr[i].ih_what,
			ppc_configed_intr[i].ih_irq,
			ppc_configed_intr[i].ih_level,
			ppc_configed_intr[i].ih_fun,
			ppc_configed_intr[i].ih_arg
			);
#endif
		macintr_establish(NULL,
			ppc_configed_intr[i].ih_irq,
			IST_LEVEL,
			ppc_configed_intr[i].ih_level,
			ppc_configed_intr[i].ih_fun,
			ppc_configed_intr[i].ih_arg,
			ppc_configed_intr[i].ih_what);
	}
}

/*
 * programmer_button function to fix args to Debugger.
 * deal with any enables/disables, if necessary.
 */
int
macintr_prog_button (void *arg)
{
#ifdef DDB
	if (db_console)
		Debugger();
#else
	printf("programmer button pressed, debugger not available\n");
#endif
	return 1;
}

/* Must be called with interrupt disable. */
void
macintr_setipl(int ipl)
{
	struct cpu_info *ci = curcpu();

	ci->ci_cpl = ipl;
	if (heathrow_FCR)
		out32rb(INT_ENABLE_REG1,
		    macintr_ienable_h[macintr_pri_share[ipl]]);

	out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]);
}

/*
 * Register an interrupt handler.
 */
void *
macintr_establish(void * lcv, int irq, int type, int level,
    int (*ih_fun)(void *), void *ih_arg, const char *name)
{
	struct cpu_info *ci = curcpu();
	struct intrq *iq;
	struct intrhand *ih;
	int s, flags;

	if (!LEGAL_IRQ(irq) || type == IST_NONE) {
		printf("%s: bogus irq %d or type %d", __func__, irq, type);
		return (NULL);
	}

	/* no point in sleeping unless someone can free memory. */
	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
	if (ih == NULL)
		panic("intr_establish: can't malloc handler info");

	iq = &macintr_handler[irq];
	switch (iq->iq_ist) {
	case IST_NONE:
		iq->iq_ist = type;
		break;
	case IST_EDGE:
		intr_shared_edge = 1;
		/* FALLTHROUGH */
	case IST_LEVEL:
		if (type == iq->iq_ist)
			break;
	case IST_PULSE:
		if (type != IST_NONE)
			panic("intr_establish: can't share %s with %s",
			    ppc_intr_typename(iq->iq_ist),
			    ppc_intr_typename(type));
		break;
	}

	flags = level & IPL_MPSAFE;
	level &= ~IPL_MPSAFE;

	KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE);

	ih->ih_fun = ih_fun;
	ih->ih_arg = ih_arg;
	ih->ih_level = level;
	ih->ih_flags = flags;
	ih->ih_irq = irq;
	evcount_attach(&ih->ih_count, name, &ih->ih_irq);

	/*
	 * Append handler to end of list
	 */
	s = ppc_intr_disable();

	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
	macintr_calc_mask();

	macintr_setipl(ci->ci_cpl);
	ppc_intr_enable(s);

	return (ih);
}

/*
 * Deregister an interrupt handler.
 */
void
macintr_disestablish(void *lcp, void *arg)
{
	struct cpu_info *ci = curcpu();
	struct intrhand *ih = arg;
	int irq = ih->ih_irq;
	int s;
	struct intrq *iq;

	if (!LEGAL_IRQ(irq)) {
		printf("%s: bogus irq %d", __func__, irq);
		return;
	}

	/*
	 * Remove the handler from the chain.
	 */

	iq = &macintr_handler[irq];
	s = ppc_intr_disable();

	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
	macintr_calc_mask();

	macintr_setipl(ci->ci_cpl);
	ppc_intr_enable(s);

	evcount_detach(&ih->ih_count);
	free((void *)ih, M_DEVBUF, 0);

	if (TAILQ_EMPTY(&iq->iq_list))
		iq->iq_ist = IST_NONE;
}

/*
 * Recalculate the interrupt masks from scratch.
 * We could code special registry and deregistry versions of this function that
 * would be faster, but the code would be nastier, and we don't expect this to
 * happen very much anyway.
 */
void
macintr_calc_mask()
{
	int irq;
	struct intrhand *ih;
	int i;

	for (i = IPL_NONE; i < IPL_NUM; i++) {
		macintr_pri_share[i] = i;
	}

	for (irq = 0; irq < ICU_LEN; irq++) {
		int maxipl = IPL_NONE;
		int minipl = IPL_HIGH;
		struct intrq *iq = &macintr_handler[irq];

		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
			if (ih->ih_level > maxipl)
				maxipl = ih->ih_level;
			if (ih->ih_level < minipl)
				minipl = ih->ih_level;
		}

		iq->iq_ipl = maxipl;

		if (maxipl == IPL_NONE) {
			minipl = IPL_NONE; /* Interrupt not enabled */
		} else {
			for (i = minipl; i < maxipl; i++)
				macintr_pri_share[i] =
				    macintr_pri_share[maxipl];
		}

		/* Enable interrupts at lower levels */

		if (irq < 32) {
			for (i = IPL_NONE; i < minipl; i++)
				macintr_ienable_l[i] |= (1 << irq);
			for (; i <= IPL_HIGH; i++)
				macintr_ienable_l[i] &= ~(1 << irq);
		} else {
			for (i = IPL_NONE; i < minipl; i++)
				macintr_ienable_h[i] |= (1 << (irq-32));
			for (; i <= IPL_HIGH; i++)
				macintr_ienable_h[i] &= ~(1 << (irq-32));
		}
	}

#if 0
	for (i = 0; i < IPL_NUM; i++)
		printf("imask[%d] %x %x\n", i, macintr_ienable_l[i],
		    macintr_ienable_h[i]);
#endif
}

/*
 * external interrupt handler
 */
void
mac_ext_intr()
{
	int irq = 0;
	int pcpl, ret;
	struct cpu_info *ci = curcpu();
	struct intrq *iq;
	struct intrhand *ih;

	pcpl = ci->ci_cpl;	/* Turn off all */

	irq = macintr_read_irq();
	while (irq != 255) {
		iq = &macintr_handler[irq];
		macintr_setipl(iq->iq_ipl);

		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
			ppc_intr_enable(1);
			ret = ((*ih->ih_fun)(ih->ih_arg));
			if (ret) {
				ih->ih_count.ec_count++;
				if (intr_shared_edge == 0 && ret == 1)
					break;
			}
			(void)ppc_intr_disable();
		}
		macintr_eoi(irq);
		macintr_setipl(pcpl);

		uvmexp.intrs++;

		irq = macintr_read_irq();
	}

	macintr_splx(pcpl);	/* Process pendings. */
}

void
macintr_eoi(int irq)
{
	u_int32_t state0, state1;

	if (irq < 32) {
		state0 =  1 << irq;
		out32rb(INT_CLEAR_REG0, state0);
	} else {
		if (heathrow_FCR) {		/* has heathrow? */
			state1 = 1 << (irq - 32);
			out32rb(INT_CLEAR_REG1, state1);
		}
	}
}

int
macintr_read_irq()
{
	struct cpu_info *ci = curcpu();
	u_int32_t state0, state1, irq_mask;
	int ipl, irq;

	state0 = in32rb(INT_STATE_REG0);

	if (heathrow_FCR)			/* has heathrow? */
		state1 = in32rb(INT_STATE_REG1);
	else
		state1 = 0;

	for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) {
		irq_mask = state0 & macintr_ienable_l[ipl];
		if (irq_mask) {
			irq = ffs(irq_mask) - 1;
			return irq;
		}
		irq_mask = state1 & macintr_ienable_h[ipl];
		if (irq_mask) {
			irq = ffs(irq_mask) + 31;
			return irq;
		}
	}
	return 255;
}


code:
/*	$OpenBSD: mediabay.c,v 1.6 2013/11/18 20:21:51 deraadt Exp $	*/
/*	$NetBSD: mediabay.c,v 1.9 2003/07/15 02:43:29 lukem Exp $	*/

/*-
 * Copyright (C) 1999 Tsubai Masanari.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <uvm/uvm_extern.h>

#include <dev/ofw/openfirm.h>

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

struct mediabay_softc {
	struct device sc_dev;
	int sc_node;
	u_int *sc_addr;
	u_int *sc_fcr;
	u_int sc_baseaddr;
	bus_space_tag_t sc_iot;
	bus_dma_tag_t sc_dmat;
	struct device *sc_content;
	struct proc *sc_kthread;
};

void mediabay_attach(struct device *, struct device *, void *);
int mediabay_match(struct device *, void *, void *);
int mediabay_print(void *, const char *);
void mediabay_attach_content(struct mediabay_softc *);
int mediabay_intr(void *);
void mediabay_create_kthread(void *);
void mediabay_kthread(void *);

struct cfattach mediabay_ca = {
	sizeof(struct mediabay_softc), mediabay_match, mediabay_attach
};
struct cfdriver mediabay_cd = {
	NULL, "mediabay", DV_DULL
};

#ifdef MEDIABAY_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif

#define FCR_MEDIABAY_RESET	0x00000002
#define FCR_MEDIABAY_IDE_ENABLE	0x00000008
#define FCR_MEDIABAY_FD_ENABLE	0x00000010
#define FCR_MEDIABAY_ENABLE	0x00000080
#define FCR_MEDIABAY_CD_POWER	0x00800000

#define MEDIABAY_ID(x)		(((x) >> 12) & 0xf)
#define MEDIABAY_ID_FD		0
#define MEDIABAY_ID_CD		3
#define MEDIABAY_ID_NONE	7

int
mediabay_match(parent, v, aux)
	struct device *parent;
	void *v;
	void *aux;
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "media-bay") == 0 &&
	    ca->ca_nintr >= 4 && ca->ca_nreg >= 8)
		return 1;

	return 0;
}

/*
 * Attach all the sub-devices we can find
 */
void
mediabay_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct mediabay_softc *sc = (struct mediabay_softc *)self;
	struct confargs *ca = aux;
	int irq, type;

	ca->ca_reg[0] += ca->ca_baseaddr;

	sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE);
	sc->sc_fcr = sc->sc_addr + 1;
	sc->sc_node = ca->ca_node;
	sc->sc_baseaddr = ca->ca_baseaddr;
	sc->sc_iot = ca->ca_iot;
	sc->sc_dmat = ca->ca_dmat;
	irq = ca->ca_intr[0];
	type = IST_LEVEL;

	if (ca->ca_nintr == 8 && ca->ca_intr[1] == 0)
		type = IST_EDGE;

	printf(" irq %d\n", irq);
	mac_intr_establish(parent, irq, type, IPL_BIO, mediabay_intr, sc,
	    "mediabay");

	kthread_create_deferred(mediabay_create_kthread, sc);

	sc->sc_content = NULL;

	if (MEDIABAY_ID(in32rb(sc->sc_addr)) != MEDIABAY_ID_NONE)
		mediabay_attach_content(sc);
}

void
mediabay_attach_content(sc)
	struct mediabay_softc *sc;
{
	int child;
	u_int fcr;
	struct device *content;
	struct confargs ca;
	u_int reg[20], intr[5];
	char name[32];

	fcr = in32rb(sc->sc_fcr);
	fcr |= FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_RESET;
	out32rb(sc->sc_fcr, fcr);
	delay(50000);

	fcr &= ~FCR_MEDIABAY_RESET;
	out32rb(sc->sc_fcr, fcr);
	delay(50000);

	fcr |= FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER;
	out32rb(sc->sc_fcr, fcr);
	delay(50000);

	for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
		bzero(name, sizeof(name));
		if (OF_getprop(child, "name", name, sizeof(name)) == -1)
			continue;
		ca.ca_name = name;
		ca.ca_node = child;
		ca.ca_baseaddr = sc->sc_baseaddr;
		ca.ca_iot = sc->sc_iot;
		ca.ca_dmat = sc->sc_dmat;

		ca.ca_nreg  = OF_getprop(child, "reg", reg, sizeof(reg));
		ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
				sizeof(intr));
		if (ca.ca_nintr == -1)
			ca.ca_nintr = OF_getprop(child, "interrupts", intr,
					sizeof(intr));
		ca.ca_reg = reg;
		ca.ca_intr = intr;

		content = config_found(&sc->sc_dev, &ca, mediabay_print);
		if (content) {
			sc->sc_content = content;
			return;
		}
	}

	/* No devices found.  Disable media-bay. */
	fcr &= ~(FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE |
		 FCR_MEDIABAY_CD_POWER | FCR_MEDIABAY_FD_ENABLE);
	out32rb(sc->sc_fcr, fcr);
}

int
mediabay_print(aux, mediabay)
	void *aux;
	const char *mediabay;
{
	struct confargs *ca = aux;

	if (mediabay == NULL && ca->ca_nreg > 0)
		printf(" offset 0x%x", ca->ca_reg[0]);

	return QUIET;
}

int
mediabay_intr(v)
	void *v;
{
	struct mediabay_softc *sc = v;

	wakeup(&sc->sc_kthread);
	return 1;
}

void
mediabay_create_kthread(v)
	void *v;
{
	struct mediabay_softc *sc = v;

	kthread_create(mediabay_kthread, sc, &sc->sc_kthread,
	    sc->sc_dev.dv_xname);
}

void
mediabay_kthread(v)
	void *v;
{
	struct mediabay_softc *sc = v;
	u_int x, fcr;

sleep:
	tsleep(&sc->sc_kthread, PRIBIO, "mbayev", 0);

	/* sleep 0.25 sec */
	tsleep(mediabay_kthread, PRIBIO, "mbayev", hz/4);

	DPRINTF("%s: ", sc->sc_dev.dv_xname);
	x = in32rb(sc->sc_addr);

	switch (MEDIABAY_ID(x)) {
	case MEDIABAY_ID_NONE:
		DPRINTF("removed\n");
		if (sc->sc_content != NULL) {
			config_detach(sc->sc_content, DETACH_FORCE);
			DPRINTF("%s: detach done\n", sc->sc_dev.dv_xname);
			sc->sc_content = NULL;

			/* disable media-bay */
			fcr = in32rb(sc->sc_fcr);
			fcr &= ~(FCR_MEDIABAY_ENABLE |
				 FCR_MEDIABAY_IDE_ENABLE |
				 FCR_MEDIABAY_CD_POWER |
				 FCR_MEDIABAY_FD_ENABLE);
			out32rb(sc->sc_fcr, fcr);
		}
		break;
	case MEDIABAY_ID_FD:
		DPRINTF("FD inserted\n");
		break;
	case MEDIABAY_ID_CD:
		DPRINTF("CD inserted\n");

		if (sc->sc_content == NULL)
			mediabay_attach_content(sc);
		break;
	default:
		printf("unknown event (0x%x)\n", x);
	}

	goto sleep;
}

/* PBG3: 0x7025X0c0 */
/* 2400: 0x0070X0a8 */

license: unknown


code:
/*	$OpenBSD: mesh.c,v 1.32 2013/12/06 21:03:05 deraadt Exp $	*/
/*	$NetBSD: mesh.c,v 1.1 1999/02/19 13:06:03 tsubai Exp $	*/

/*-
 * Copyright (c) 2000	Tsubai Masanari.
 * Copyright (c) 1999	Internet Research Institute, Inc.
 * 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 by
 *	Internet Research Institute, Inc.
 * 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.
 */

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

#include <uvm/uvm_extern.h>

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_message.h>

#include <dev/ofw/openfirm.h>

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

#include <macppc/dev/dbdma.h>

#ifdef MESH_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif

/* MESH register offsets */
#define MESH_XFER_COUNT0	0x00	/* transfer count (low)  */
#define MESH_XFER_COUNT1	0x10	/* transfer count (high) */
#define MESH_FIFO		0x20	/* FIFO (16byte depth) */
#define MESH_SEQUENCE		0x30	/* command register */
#define MESH_BUS_STATUS0	0x40
#define MESH_BUS_STATUS1	0x50
#define MESH_FIFO_COUNT		0x60
#define MESH_EXCEPTION		0x70
#define MESH_ERROR		0x80
#define MESH_INTR_MASK		0x90
#define MESH_INTERRUPT		0xa0
#define MESH_SOURCE_ID		0xb0
#define MESH_DEST_ID		0xc0
#define MESH_SYNC_PARAM		0xd0
#define MESH_MESH_ID		0xe0	/* MESH version */
#define MESH_SEL_TIMEOUT	0xf0	/* selection timeout delay */

#define MESH_SIGNATURE		0xe2	/* XXX wrong! */

/* MESH commands */
#define MESH_CMD_ARBITRATE	0x01
#define MESH_CMD_SELECT		0x02
#define MESH_CMD_COMMAND	0x03
#define MESH_CMD_STATUS		0x04
#define MESH_CMD_DATAOUT	0x05
#define MESH_CMD_DATAIN		0x06
#define MESH_CMD_MSGOUT		0x07
#define MESH_CMD_MSGIN		0x08
#define MESH_CMD_BUSFREE	0x09
#define MESH_CMD_ENABLE_PARITY	0x0A
#define MESH_CMD_DISABLE_PARITY	0x0B
#define MESH_CMD_ENABLE_RESEL	0x0C
#define MESH_CMD_DISABLE_RESEL	0x0D
#define MESH_CMD_RESET_MESH	0x0E
#define MESH_CMD_FLUSH_FIFO	0x0F
#define MESH_SEQ_DMA		0x80
#define MESH_SEQ_TARGET		0x40
#define MESH_SEQ_ATN		0x20
#define MESH_SEQ_ACTNEG		0x10

/* INTERRUPT/INTR_MASK register bits */
#define MESH_INTR_ERROR		0x04
#define MESH_INTR_EXCEPTION	0x02
#define MESH_INTR_CMDDONE	0x01

/* EXCEPTION register bits */
#define MESH_EXC_SELATN		0x20	/* selected and ATN asserted (T) */
#define MESH_EXC_SELECTED	0x10	/* selected (T) */
#define MESH_EXC_RESEL		0x08	/* reselected */
#define MESH_EXC_ARBLOST	0x04	/* arbitration lost */
#define MESH_EXC_PHASEMM	0x02	/* phase mismatch */
#define MESH_EXC_SELTO		0x01	/* selection timeout */

/* ERROR register bits */
#define MESH_ERR_DISCONNECT	0x40	/* unexpected disconnect */
#define MESH_ERR_SCSI_RESET	0x20	/* Rst signal asserted */
#define MESH_ERR_SEQERR		0x10	/* sequence error */
#define MESH_ERR_PARITY_ERR3	0x08	/* parity error */
#define MESH_ERR_PARITY_ERR2	0x04
#define MESH_ERR_PARITY_ERR1	0x02
#define MESH_ERR_PARITY_ERR0	0x01

/* BUS_STATUS0 status bits */
#define MESH_STATUS0_REQ32	0x80
#define MESH_STATUS0_ACK32	0x40
#define MESH_STATUS0_REQ	0x20
#define MESH_STATUS0_ACK	0x10
#define MESH_STATUS0_ATN	0x08
#define MESH_STATUS0_MSG	0x04
#define MESH_STATUS0_CD		0x02
#define MESH_STATUS0_IO		0x01

/* BUS_STATUS1 status bits */
#define MESH_STATUS1_RST	0x80
#define MESH_STATUS1_BSY	0x40
#define MESH_STATUS1_SEL	0x20

#define T_SYNCMODE 0x01		/* target uses sync mode */
#define T_SYNCNEGO 0x02		/* sync negotiation done */

struct mesh_tinfo {
	int flags;
	int period;
	int offset;
};

/* scb flags */
#define MESH_POLL		0x01
#define MESH_CHECK		0x02
#define MESH_SENSE		0x04
#define MESH_READ		0x80

struct mesh_scb {
	TAILQ_ENTRY(mesh_scb) chain;
	int flags;
	struct scsi_xfer *xs;
	struct scsi_generic cmd;
	int cmdlen;
	int target;			/* target SCSI ID */
	int resid;
	void *daddr;
	vsize_t dlen;
	int status;
};

/* sc_flags value */
#define MESH_DMA_ACTIVE	0x01

#define MESH_DMALIST_MAX	32

struct mesh_softc {
	struct device sc_dev;		/* us as a device */
	struct scsi_link sc_link;

	u_char *sc_reg;			/* MESH base address */
	bus_dmamap_t sc_dmamap;
	bus_dma_tag_t sc_dmat;
	struct dbdma_regmap *sc_dmareg;	/* DMA register address */
	struct dbdma_command *sc_dmacmd;	/* DMA command area */
	dbdma_t sc_dbdma;

	int sc_flags;
	int sc_cfflags;			/* copy of config flags */
	int sc_minsync;			/* minimum sync period */
	int sc_irq;
	int sc_freq;			/* SCSI bus frequency in MHz */
	int sc_id;			/* our SCSI ID */
	struct mesh_tinfo sc_tinfo[8];	/* target information */

	int sc_nextstate;
	int sc_prevphase;
	struct mesh_scb *sc_nexus;	/* current command */

	int sc_msgout;
	int sc_imsglen;
	u_char sc_imsg[16];
	u_char sc_omsg[16];

	TAILQ_HEAD(, mesh_scb) free_scb;
	TAILQ_HEAD(, mesh_scb) ready_scb;
	struct mesh_scb sc_scb[16];

	struct timeout sc_tmo;

	struct mutex		sc_scb_mtx;
	struct scsi_iopool	sc_iopool;
};

/* mesh_msgout() values */
#define SEND_REJECT	1
#define SEND_IDENTIFY	2
#define SEND_SDTR	4

static inline int mesh_read_reg(struct mesh_softc *, int);
static inline void mesh_set_reg(struct mesh_softc *, int, int);

int mesh_match(struct device *, void *, void *);
void mesh_attach(struct device *, struct device *, void *);
int mesh_activate(struct device *, int);

int mesh_intr(void *);
void mesh_error(struct mesh_softc *, struct mesh_scb *, int, int);
void mesh_select(struct mesh_softc *, struct mesh_scb *);
void mesh_identify(struct mesh_softc *, struct mesh_scb *);
void mesh_command(struct mesh_softc *, struct mesh_scb *);
int mesh_dma_setup(struct mesh_softc *, struct mesh_scb *);
int mesh_dataio(struct mesh_softc *, struct mesh_scb *);
void mesh_status(struct mesh_softc *, struct mesh_scb *);
void mesh_msgin(struct mesh_softc *, struct mesh_scb *);
void mesh_msgout(struct mesh_softc *, int);
void mesh_bus_reset(struct mesh_softc *);
void mesh_reset(struct mesh_softc *);
int mesh_stp(struct mesh_softc *, int);
void mesh_setsync(struct mesh_softc *, struct mesh_tinfo *);
struct mesh_scb *mesh_get_scb(struct mesh_softc *);
void *mesh_scb_alloc(void *);
void mesh_scb_free(void *, void *);
void mesh_scsi_cmd(struct scsi_xfer *);
void mesh_sched(struct mesh_softc *);
int mesh_poll(struct scsi_xfer *);
void mesh_done(struct mesh_softc *, struct mesh_scb *);
void mesh_timeout(void *);
void mesh_minphys(struct buf *, struct scsi_link *);

struct cfattach mesh_ca = {
	sizeof(struct mesh_softc), mesh_match, mesh_attach,
	NULL, mesh_activate
};

struct cfdriver mesh_cd = {
	NULL, "mesh", DV_DULL
};

struct scsi_adapter mesh_switch = {
	mesh_scsi_cmd, mesh_minphys, NULL, NULL
};

#define MESH_DATAOUT	0
#define MESH_DATAIN	MESH_STATUS0_IO
#define MESH_COMMAND	MESH_STATUS0_CD
#define MESH_STATUS	(MESH_STATUS0_CD | MESH_STATUS0_IO)
#define MESH_MSGOUT	(MESH_STATUS0_MSG | MESH_STATUS0_CD)
#define MESH_MSGIN	(MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO)

#define MESH_SELECTING	8
#define MESH_IDENTIFY	9
#define MESH_COMPLETE	10
#define MESH_BUSFREE	11
#define MESH_UNKNOWN	-1

#define MESH_PHASE_MASK	(MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO)

int
mesh_match(struct device *parent, void *vcf, void *aux)
{
	struct confargs *ca = aux;
	char compat[32];

	if (strcmp(ca->ca_name, "mesh") == 0)
		return 1;

	bzero(compat, sizeof(compat));
	OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
	if (strcmp(compat, "chrp,mesh0") == 0)
		return 1;

	return 0;
}

void
mesh_attach(struct device *parent, struct device *self, void *aux)
{
	struct mesh_softc *sc = (void *)self;
	struct confargs *ca = aux;
	struct scsibus_attach_args saa;
	int i, error;
	u_int *reg;

	reg = ca->ca_reg;
	reg[0] += ca->ca_baseaddr;
	reg[2] += ca->ca_baseaddr;
	if ((sc->sc_reg = mapiodev(reg[0], reg[1])) == NULL) {
		printf(": cannot map device registers\n");
		return;
	}

	sc->sc_irq = ca->ca_intr[0];
	if ((sc->sc_dmareg = mapiodev(reg[2], reg[3])) == NULL) {
		printf(": cannot map DMA registers\n");
		goto noreg;
	}

	sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags;

	if (OF_getprop(ca->ca_node, "clock-frequency", &sc->sc_freq, 4) != 4) {
		printf(": cannot get clock-frequency\n");
		goto nofreq;
	}

	sc->sc_dmat = ca->ca_dmat;
	if ((error = bus_dmamap_create(sc->sc_dmat,
	    MESH_DMALIST_MAX * DBDMA_COUNT_MAX, MESH_DMALIST_MAX,
	    DBDMA_COUNT_MAX, NBPG, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
		printf(": cannot create DMA map, error = %d\n", error);
		goto nofreq;
	}

	sc->sc_freq /= 1000000;	/* in MHz */
	sc->sc_minsync = 25;	/* maximum sync rate = 10MB/sec */
	sc->sc_id = 7;

	TAILQ_INIT(&sc->free_scb);
	TAILQ_INIT(&sc->ready_scb);
	mtx_init(&sc->sc_scb_mtx, IPL_BIO);
	scsi_iopool_init(&sc->sc_iopool, sc, mesh_scb_alloc, mesh_scb_free);
	for (i = 0; i < sizeof(sc->sc_scb)/sizeof(sc->sc_scb[0]); i++)
		TAILQ_INSERT_TAIL(&sc->free_scb, &sc->sc_scb[i], chain);

	if ((sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, MESH_DMALIST_MAX))
	    == NULL) {
		printf(": cannot alloc dma descriptors\n");
		goto nodbdma;
	}

	sc->sc_dmacmd = sc->sc_dbdma->d_addr;
	timeout_set(&sc->sc_tmo, mesh_timeout, sc);

	mesh_reset(sc);
	mesh_bus_reset(sc);

	printf(" irq %d: %dMHz\n", sc->sc_irq, sc->sc_freq);

	sc->sc_link.adapter_softc = sc;
	sc->sc_link.adapter_target = sc->sc_id;
	sc->sc_link.adapter = &mesh_switch;
	sc->sc_link.openings = 2;
	sc->sc_link.pool = &sc->sc_iopool;

	bzero(&saa, sizeof(saa));
	saa.saa_sc_link = &sc->sc_link;

	config_found(&sc->sc_dev, &saa, scsiprint);

	mac_intr_establish(parent, sc->sc_irq, IST_LEVEL, IPL_BIO, mesh_intr,
	    sc, sc->sc_dev.dv_xname);

	return;
nodbdma:
	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
nofreq:
	unmapiodev(sc->sc_dmareg, reg[3]);
noreg:
	unmapiodev(sc->sc_reg, reg[1]);
}

int
mesh_activate(struct device *self, int act)
{
	struct mesh_softc *sc = (struct mesh_softc *)self;
	int rv = 0;

	switch (act) {
	case DVACT_POWERDOWN:
		rv = config_activate_children(self, act);
		/* Set to async mode. */
		mesh_set_reg(sc, MESH_SYNC_PARAM, 2);
		mesh_bus_reset(sc);
		break;
	default:
		rv = config_activate_children(self, act);
		break;
	}
	return (rv);
}

#define MESH_SET_XFER(sc, count) do {					\
	mesh_set_reg(sc, MESH_XFER_COUNT0, count);			\
	mesh_set_reg(sc, MESH_XFER_COUNT1, count >> 8);			\
} while (0)

#define MESH_GET_XFER(sc) ((mesh_read_reg(sc, MESH_XFER_COUNT1) << 8) |	\
			   mesh_read_reg(sc, MESH_XFER_COUNT0))

int
mesh_read_reg(struct mesh_softc *sc, int reg)
{
	return in8(sc->sc_reg + reg);
}

void
mesh_set_reg(struct mesh_softc *sc, int reg, int val)
{
	out8(sc->sc_reg + reg, val);
}

#ifdef MESH_DEBUG
static char scsi_phase[][8] = {
	"DATAOUT",
	"DATAIN",
	"COMMAND",
	"STATUS",
	"",
	"",
	"MSGOUT",
	"MSGIN"
};
#endif

int
mesh_intr(void *arg)
{
	struct mesh_softc *sc = arg;
	struct mesh_scb *scb;
	int fifocnt;
	u_char intr, exception, error, status0, status1;

	intr = mesh_read_reg(sc, MESH_INTERRUPT);

	if (intr == 0) {
		DPRINTF("%s: stray interrupt\n", sc->sc_dev.dv_xname);
		return 0;
	}
	exception = mesh_read_reg(sc, MESH_EXCEPTION);
	error = mesh_read_reg(sc, MESH_ERROR);
	status0 = mesh_read_reg(sc, MESH_BUS_STATUS0);
	status1 = mesh_read_reg(sc, MESH_BUS_STATUS1);

	/* clear interrupt */
	mesh_set_reg(sc, MESH_INTERRUPT, intr);

#ifdef MESH_DEBUG
{

	printf("mesh_intr status0 = 0x%x (%s), exc = 0x%x\n",
	    status0, scsi_phase[status0 & 7], exception);
}
#endif

	scb = sc->sc_nexus;
	if (scb == NULL) {
		DPRINTF("%s: NULL nexus\n", sc->sc_dev.dv_xname);
		return 1;
	}

	if (intr & MESH_INTR_CMDDONE) {
		if (sc->sc_flags & MESH_DMA_ACTIVE) {
			dbdma_stop(sc->sc_dmareg);
			bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);

			sc->sc_flags &= ~MESH_DMA_ACTIVE;
			scb->resid = MESH_GET_XFER(sc);

			fifocnt = mesh_read_reg(sc, MESH_FIFO_COUNT);
			if (fifocnt != 0) {
				if (scb->flags & MESH_READ) {
					char *cp;

					cp = (char *)scb->daddr + scb->dlen
					    - fifocnt;
					DPRINTF("fifocnt = %d, resid = %d\n",
					    fifocnt, scb->resid);
					while (fifocnt > 0) {
						*cp++ = mesh_read_reg(sc,
						    MESH_FIFO);
						fifocnt--;
					}
				} else {
					mesh_set_reg(sc, MESH_SEQUENCE,
					    MESH_CMD_FLUSH_FIFO);
				}
			} else {
				/* Clear all interrupts */
				mesh_set_reg(sc, MESH_INTERRUPT, 7);
			}
		}
	}

	if (intr & MESH_INTR_ERROR) {
		printf("%s: error %02x %02x\n",
		    sc->sc_dev.dv_xname, error, exception);
		mesh_error(sc, scb, error, 0);
		return 1;
	}

	if (intr & MESH_INTR_EXCEPTION) {
		/* selection timeout */
		if (exception & MESH_EXC_SELTO) {
			mesh_error(sc, scb, 0, exception);
			return 1;
		}

		/* phase mismatch */
		if (exception & MESH_EXC_PHASEMM) {
			DPRINTF("%s: PHASE MISMATCH; nextstate = %d -> ",
			    sc->sc_dev.dv_xname, sc->sc_nextstate);
			sc->sc_nextstate = status0 & MESH_PHASE_MASK;

			DPRINTF("%d, resid = %d\n",
			    sc->sc_nextstate, scb->resid);
		}
	}

	if (sc->sc_nextstate == MESH_UNKNOWN)
		sc->sc_nextstate = status0 & MESH_PHASE_MASK;

	switch (sc->sc_nextstate) {

	case MESH_IDENTIFY:
		mesh_identify(sc, scb);
		break;
	case MESH_COMMAND:
		mesh_command(sc, scb);
		break;
	case MESH_DATAIN:
	case MESH_DATAOUT:
		if (mesh_dataio(sc, scb)) {
			scb->xs->error = XS_DRIVER_STUFFUP;
			mesh_done(sc, scb);
		}
		break;
	case MESH_STATUS:
		mesh_status(sc, scb);
		break;
	case MESH_MSGIN:
		mesh_msgin(sc, scb);
		break;
	case MESH_COMPLETE:
		mesh_done(sc, scb);
		break;

	default:
		printf("%s: unknown state (%d)\n", sc->sc_dev.dv_xname,
		    sc->sc_nextstate);
		scb->xs->error = XS_DRIVER_STUFFUP;
		mesh_done(sc, scb);
	}

	return 1;
}

void
mesh_error(struct mesh_softc *sc, struct mesh_scb *scb, int error,
    int exception)
{
	if (error & MESH_ERR_SCSI_RESET) {
		printf("%s: SCSI RESET\n", sc->sc_dev.dv_xname);

		/* Wait until the RST signal is deasserted. */
		while (mesh_read_reg(sc, MESH_BUS_STATUS1) & MESH_STATUS1_RST);
			mesh_reset(sc);
		return;
	}

	if (error & MESH_ERR_PARITY_ERR0) {
		printf("%s: parity error\n", sc->sc_dev.dv_xname);
		scb->xs->error = XS_DRIVER_STUFFUP;
	}

	if (error & MESH_ERR_DISCONNECT) {
		printf("%s: unexpected disconnect\n", sc->sc_dev.dv_xname);
		if (sc->sc_nextstate != MESH_COMPLETE)
			scb->xs->error = XS_DRIVER_STUFFUP;
	}

	if (exception & MESH_EXC_SELTO) {
		/* XXX should reset bus here? */
		scb->xs->error = XS_SELTIMEOUT;
	}

	mesh_done(sc, scb);
}

void
mesh_select(struct mesh_softc *sc, struct mesh_scb *scb)
{
	struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target];

	DPRINTF("mesh_select\n");

	mesh_setsync(sc, ti);
	MESH_SET_XFER(sc, 0);

	/* arbitration */

	/*
	 * MESH mistakenly asserts TARGET ID bit along with its own ID bit
	 * in arbitration phase (like selection).  So we should load
	 * initiator ID to DestID register temporarily.
	 */
	mesh_set_reg(sc, MESH_DEST_ID, sc->sc_id);
	mesh_set_reg(sc, MESH_INTR_MASK, 0);	/* disable intr. */
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ARBITRATE);

	while (mesh_read_reg(sc, MESH_INTERRUPT) == 0);
	mesh_set_reg(sc, MESH_INTERRUPT, 1);
	mesh_set_reg(sc, MESH_INTR_MASK, 7);

	/* selection */
	mesh_set_reg(sc, MESH_DEST_ID, scb->target);
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_SELECT | MESH_SEQ_ATN);

	sc->sc_prevphase = MESH_SELECTING;
	sc->sc_nextstate = MESH_IDENTIFY;

	timeout_add_sec(&sc->sc_tmo, 10);
}

void
mesh_identify(struct mesh_softc *sc, struct mesh_scb *scb)
{
	struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target];

	DPRINTF("mesh_identify\n");
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO);

	if ((ti->flags & T_SYNCNEGO) == 0) {
		ti->period = sc->sc_minsync;
		ti->offset = 15;
		mesh_msgout(sc, SEND_IDENTIFY | SEND_SDTR);
		sc->sc_nextstate = MESH_MSGIN;
	} else {
		mesh_msgout(sc, SEND_IDENTIFY);
		sc->sc_nextstate = MESH_COMMAND;
	}
}

void
mesh_command(struct mesh_softc *sc, struct mesh_scb *scb)
{
	int i;
	char *cmdp;

#ifdef MESH_DEBUG
	printf("mesh_command cdb = %02x", scb->cmd.opcode);
	for (i = 0; i < 5; i++)
		printf(" %02x", scb->cmd.bytes[i]);
	printf("\n");
#endif

	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO);

	MESH_SET_XFER(sc, scb->cmdlen);
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_COMMAND);

	cmdp = (char *)&scb->cmd;
	for (i = 0; i < scb->cmdlen; i++)
		mesh_set_reg(sc, MESH_FIFO, *cmdp++);

	if (scb->resid == 0)
		sc->sc_nextstate = MESH_STATUS;		/* no data xfer */
	else
		sc->sc_nextstate = MESH_DATAIN;
}

int
mesh_dma_setup(struct mesh_softc *sc, struct mesh_scb *scb)
{
	int datain = scb->flags & MESH_READ;
	struct dbdma_command *cmdp;
	u_int cmd;
	int i, error;

	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, scb->daddr,
	    scb->dlen, NULL, BUS_DMA_NOWAIT)) != 0)
		return (error);

	cmdp = sc->sc_dmacmd;
	cmd = datain ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE;

	for (i = 0; i < sc->sc_dmamap->dm_nsegs; i++, cmdp++) {
		if (i + 1 == sc->sc_dmamap->dm_nsegs)
			cmd = datain ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST;
		DBDMA_BUILD(cmdp, cmd, 0, sc->sc_dmamap->dm_segs[i].ds_len,
		    sc->sc_dmamap->dm_segs[i].ds_addr, DBDMA_INT_NEVER,
		    DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
	}

	DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0,
	    DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);

	return(0);
}

int
mesh_dataio(struct mesh_softc *sc, struct mesh_scb *scb)
{
	int error;

	if ((error = mesh_dma_setup(sc, scb)))
		return(error);

	if (scb->dlen == 65536)
		MESH_SET_XFER(sc, 0);	/* TC = 0 means 64KB transfer */
	else
		MESH_SET_XFER(sc, scb->dlen);

	if (scb->flags & MESH_READ)
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAIN | MESH_SEQ_DMA);
	else
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAOUT | MESH_SEQ_DMA);

	dbdma_start(sc->sc_dmareg, sc->sc_dbdma);
	sc->sc_flags |= MESH_DMA_ACTIVE;
	sc->sc_nextstate = MESH_STATUS;

	return(0);
}

void
mesh_status(struct mesh_softc *sc, struct mesh_scb *scb)
{
	if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) {	/* XXX cheat */
		DPRINTF("mesh_status(0)\n");
		MESH_SET_XFER(sc, 1);
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_STATUS);
		sc->sc_nextstate = MESH_STATUS;
		return;
	}

	scb->status = mesh_read_reg(sc, MESH_FIFO);
	DPRINTF("mesh_status(1): status = 0x%x\n", scb->status);
	if (mesh_read_reg(sc, MESH_FIFO_COUNT) != 0)
		DPRINTF("FIFO_COUNT=%d\n", mesh_read_reg(sc, MESH_FIFO_COUNT));

	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO);
	MESH_SET_XFER(sc, 1);
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN);

	sc->sc_nextstate = MESH_MSGIN;
}

void
mesh_msgin(struct mesh_softc *sc, struct mesh_scb *scb)
{
	DPRINTF("mesh_msgin\n");

	if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) {	/* XXX cheat */
		MESH_SET_XFER(sc, 1);
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN);
		sc->sc_imsglen = 0;
		sc->sc_nextstate = MESH_MSGIN;
		return;
	}

	sc->sc_imsg[sc->sc_imsglen++] = mesh_read_reg(sc, MESH_FIFO);

	if (sc->sc_imsglen == 1 && IS1BYTEMSG(sc->sc_imsg[0]))
		goto gotit;
	if (sc->sc_imsglen == 2 && IS2BYTEMSG(sc->sc_imsg[0]))
		goto gotit;
	if (sc->sc_imsglen >= 3 && ISEXTMSG(sc->sc_imsg[0]) &&
	    sc->sc_imsglen == sc->sc_imsg[1] + 2)
		goto gotit;

	sc->sc_nextstate = MESH_MSGIN;
	MESH_SET_XFER(sc, 1);
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN);
	return;

gotit:
#ifdef MESH_DEBUG
	{
		int i;
		printf("msgin:");
		for (i = 0; i < sc->sc_imsglen; i++)
			printf(" 0x%02x", sc->sc_imsg[i]);
		printf("\n");
	}
#endif

	switch (sc->sc_imsg[0]) {
	case MSG_CMDCOMPLETE:
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE);
		sc->sc_nextstate = MESH_COMPLETE;
		sc->sc_imsglen = 0;
		return;

	case MSG_MESSAGE_REJECT:
		if (sc->sc_msgout & SEND_SDTR) {
			printf("SDTR rejected\n");
			printf("using async mode\n");
			sc->sc_tinfo[scb->target].period = 0;
			sc->sc_tinfo[scb->target].offset = 0;
			mesh_setsync(sc, &sc->sc_tinfo[scb->target]);
			break;
		}
		break;

	case MSG_NOOP:
		break;

	case MSG_EXTENDED:
		goto extended_msg;

	default:
		sc_print_addr(scb->xs->sc_link);
		printf("unrecognized MESSAGE(0x%02x); sending REJECT\n",
		    sc->sc_imsg[0]);

	reject:
		mesh_msgout(sc, SEND_REJECT);
		return;
	}
	goto done;

extended_msg:
	/* process an extended message */
	switch (sc->sc_imsg[2]) {
	case MSG_EXT_SDTR:
	  {
		struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target];
		int period = sc->sc_imsg[3];
		int offset = sc->sc_imsg[4];
		int r = 250 / period;
		int s = (100*250) / period - 100 * r;

		if (period < sc->sc_minsync) {
			ti->period = sc->sc_minsync;
			ti->offset = 15;
			mesh_msgout(sc, SEND_SDTR);
			return;
		}
		sc_print_addr(scb->xs->sc_link);
		/* XXX if (offset != 0) ... */
		printf("max sync rate %d.%02dMb/s\n", r, s);
		ti->period = period;
		ti->offset = offset;
		ti->flags |= T_SYNCNEGO;
		ti->flags |= T_SYNCMODE;
		mesh_setsync(sc, ti);
		goto done;
	  }
	default:
		printf("%s target %d: rejecting extended message 0x%x\n",
		    sc->sc_dev.dv_xname, scb->target, sc->sc_imsg[0]);
		goto reject;
	}

done:
	sc->sc_imsglen = 0;
	sc->sc_nextstate = MESH_UNKNOWN;

	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); /* XXX really? */
}

void
mesh_msgout(struct mesh_softc *sc, int msg)
{
	struct mesh_scb *scb = sc->sc_nexus;
	struct mesh_tinfo *ti;
	int lun, len, i;

	DPRINTF("mesh_msgout: sending");

	sc->sc_msgout = msg;
	len = 0;

	if (msg & SEND_REJECT) {
		DPRINTF(" REJECT");
		sc->sc_omsg[len++] = MSG_MESSAGE_REJECT;
	}
	if (msg & SEND_IDENTIFY) {
		DPRINTF(" IDENTIFY");
		lun = scb->xs->sc_link->lun;
		sc->sc_omsg[len++] = MSG_IDENTIFY(lun, 0);
	}
	if (msg & SEND_SDTR) {
		DPRINTF(" SDTR");
		ti = &sc->sc_tinfo[scb->target];
		sc->sc_omsg[len++] = MSG_EXTENDED;
		sc->sc_omsg[len++] = 3;
		sc->sc_omsg[len++] = MSG_EXT_SDTR;
		sc->sc_omsg[len++] = ti->period;
		sc->sc_omsg[len++] = ti->offset;
	}
	DPRINTF("\n");

	MESH_SET_XFER(sc, len);
	if (len == 1) {
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGOUT);
		mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[0]);
	} else {
		mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGOUT | MESH_SEQ_ATN);

		for (i = 0; i < len - 1; i++)
			mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[i]);

		/* Wait for the FIFO empty... */
		while (mesh_read_reg(sc, MESH_FIFO_COUNT) > 0);

		/* ...then write the last byte. */
		mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[i]);
	}
	sc->sc_nextstate = MESH_UNKNOWN;
}

void
mesh_bus_reset(struct mesh_softc *sc)
{
	DPRINTF("mesh_bus_reset\n");

	/* Disable interrupts. */
	mesh_set_reg(sc, MESH_INTR_MASK, 0);

	/* Assert RST line. */
	mesh_set_reg(sc, MESH_BUS_STATUS1, MESH_STATUS1_RST);
	delay(50);
	mesh_set_reg(sc, MESH_BUS_STATUS1, 0);

	mesh_reset(sc);
}

void
mesh_reset(struct mesh_softc *sc)
{
	int i;

	DPRINTF("mesh_reset\n");

	/* Reset DMA first. */
	dbdma_reset(sc->sc_dmareg);

	/* Disable interrupts. */
	mesh_set_reg(sc, MESH_INTR_MASK, 0);

	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_RESET_MESH);
	delay(1);

	/* Wait for reset done. */
	while (mesh_read_reg(sc, MESH_INTERRUPT) == 0);

	/* Clear interrupts */
	mesh_set_reg(sc, MESH_INTERRUPT, 0x7);

	/* Set SCSI ID */
	mesh_set_reg(sc, MESH_SOURCE_ID, sc->sc_id);

	/* Set to async mode by default. */
	mesh_set_reg(sc, MESH_SYNC_PARAM, 2);

	/* Set selection timeout to 250ms. */
	mesh_set_reg(sc, MESH_SEL_TIMEOUT, 250 * sc->sc_freq / 500);

	/* Enable parity check. */
	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ENABLE_PARITY);

	/* Enable all interrupts. */
	mesh_set_reg(sc, MESH_INTR_MASK, 0x7);

	for (i = 0; i < 7; i++) {
		struct mesh_tinfo *ti = &sc->sc_tinfo[i];

		ti->flags = 0;
		ti->period = ti->offset = 0;
		if (sc->sc_cfflags & (0x100 << i))
			ti->flags |= T_SYNCNEGO;
	}
	sc->sc_nexus = NULL;
}

int
mesh_stp(struct mesh_softc *sc, int v)
{
	/*
	 * stp(v) = 5 * clock_period	 (v == 0)
	 *	= (v + 2) * 2 clock_period (v > 0)
	 */

	if (v == 0)
		return 5 * 250 / sc->sc_freq;
	else
		return (v + 2) * 2 * 250 / sc->sc_freq;
}

void
mesh_setsync(struct mesh_softc *sc, struct mesh_tinfo *ti)
{
	int period = ti->period;
	int offset = ti->offset;
	int v;

	if ((ti->flags & T_SYNCMODE) == 0)
		offset = 0;

	if (offset == 0) {	/* async mode */
		mesh_set_reg(sc, MESH_SYNC_PARAM, 2);
		return;
	}

	v = period * sc->sc_freq / 250 / 2 - 2;
	if (v < 0)
		v = 0;
	if (mesh_stp(sc, v) < period)
		v++;
	if (v > 15)
		v = 15;
	mesh_set_reg(sc, MESH_SYNC_PARAM, (offset << 4) | v);
}

void *
mesh_scb_alloc(void *xsc)
{
	struct mesh_softc *sc = xsc;
	struct mesh_scb *scb;

	mtx_enter(&sc->sc_scb_mtx);
	scb = TAILQ_FIRST(&sc->free_scb);
	if (scb)
		TAILQ_REMOVE(&sc->free_scb, scb, chain);
	mtx_leave(&sc->sc_scb_mtx);

	return scb;
}

void
mesh_scb_free(void *xsc, void *xscb)
{
	struct mesh_softc *sc = xsc;
	struct mesh_scb *scb = xscb;

	mtx_enter(&sc->sc_scb_mtx);
	TAILQ_INSERT_TAIL(&sc->free_scb, scb, chain);
	mtx_leave(&sc->sc_scb_mtx);
}

void
mesh_scsi_cmd(struct scsi_xfer *xs)
{
	struct scsi_link *sc_link = xs->sc_link;;
	struct mesh_softc *sc = sc_link->adapter_softc;
	struct mesh_scb *scb;
	u_int flags;
	int s;

	flags = xs->flags;
	scb = xs->io;

	DPRINTF("cmdlen: %d\n", xs->cmdlen);
	scb->xs = xs;
	scb->flags = 0;
	scb->status = 0;
	scb->daddr = xs->data;
	scb->dlen = xs->datalen;
	scb->resid = xs->datalen;
	bcopy(xs->cmd, &scb->cmd, xs->cmdlen);
	scb->cmdlen = xs->cmdlen;
	scb->target = sc_link->target;
	sc->sc_imsglen = 0;	/* XXX ? */

	if (flags & SCSI_POLL)
		scb->flags |= MESH_POLL;

	if (flags & SCSI_DATA_IN)
		scb->flags |= MESH_READ;

	s = splbio();
	TAILQ_INSERT_TAIL(&sc->ready_scb, scb, chain);
	if (sc->sc_nexus == NULL)
		mesh_sched(sc);
	splx(s);

	if (xs->flags & SCSI_POLL) {
		if (mesh_poll(xs)) {
			printf("%s: poll timeout\n",
			    sc->sc_dev.dv_xname);

		}
	}
}

void
mesh_sched(struct mesh_softc *sc)
{
	struct mesh_scb *scb;

	TAILQ_FOREACH(scb, &sc->ready_scb, chain) {
		if (sc->sc_nexus == NULL) {
			TAILQ_REMOVE(&sc->ready_scb, scb, chain);
			sc->sc_nexus = scb;
			mesh_select(sc, scb);
			return;
		}
	}
}

int
mesh_poll(struct scsi_xfer *xs)
{
	struct scsi_link *link = xs->sc_link;
	struct mesh_softc *sc = link->adapter_softc;

	int count = xs->timeout;
	while (count) {
		if (mesh_read_reg(sc, MESH_INTERRUPT))
			mesh_intr(sc);

		if (xs->flags & ITSDONE)
			return 0;
		DELAY(1000);
		count--;
	}

	return 1;
}

void
mesh_done(struct mesh_softc *sc, struct mesh_scb *scb)
{
	struct scsi_xfer *xs = scb->xs;

	DPRINTF("mesh_done\n");

	sc->sc_nextstate = MESH_BUSFREE;
	sc->sc_nexus = NULL;

	timeout_del(&sc->sc_tmo);

	if (scb->status == SCSI_BUSY) {
		xs->error = XS_BUSY;
		printf("Target busy\n");
	}

	if (scb->status == SCSI_CHECK)
		xs->error = XS_BUSY;

	xs->status = scb->status;
	xs->resid = scb->resid;

	mesh_set_reg(sc, MESH_SYNC_PARAM, 2);

	if ((xs->flags & SCSI_POLL) == 0)
		mesh_sched(sc);

	scsi_done(xs);
}

void
mesh_timeout(void *arg)
{

	struct mesh_softc *sc = arg;
	struct mesh_scb *scb = sc->sc_nexus;
	int s;
	int status0, status1;
	int intr, error, exception, imsk;

	printf("%s: timeout state %d\n", sc->sc_dev.dv_xname, sc->sc_nextstate);

	intr = mesh_read_reg(sc, MESH_INTERRUPT);
	imsk = mesh_read_reg(sc, MESH_INTR_MASK);
	exception = mesh_read_reg(sc, MESH_EXCEPTION);
	error = mesh_read_reg(sc, MESH_ERROR);
	status0 = mesh_read_reg(sc, MESH_BUS_STATUS0);
	status1 = mesh_read_reg(sc, MESH_BUS_STATUS1);

	s = splbio();
	if (sc->sc_flags & MESH_DMA_ACTIVE) {
		dbdma_reset(sc->sc_dmareg);
	}
	scb->xs->error = XS_TIMEOUT;

	mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE);
	sc->sc_nextstate = MESH_COMPLETE;

	splx(s);
}

void
mesh_minphys(struct buf *bp, struct scsi_link *sl)
{
	if (bp->b_bcount > 64*1024)
		bp->b_bcount = 64*1024;

	minphys(bp);
}


code:
/*	$OpenBSD: onyx.c,v 1.10 2009/10/26 20:17:27 deraadt Exp $	*/

/*-
 * Copyright (c) 2005 Tsubai Masanari.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * http://focus.ti.com/docs/prod/folders/print/pcm3052.html
 *
 * Datasheet is available from
 * http://focus.ti.com/docs/prod/folders/print/pcm3052a.html
 */

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

#include <dev/audio_if.h>
#include <dev/ofw/openfirm.h>
#include <macppc/dev/dbdma.h>

#include <machine/autoconf.h>

#include <macppc/dev/i2svar.h>
#include <macppc/dev/kiicvar.h>

#ifdef ONYX_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif

/* XXX */
#define PCM3052_I2C_ADDR	0x8c

/* PCM3052 registers */
#define PCM3052_REG_LEFT_VOLUME		0x41
#define PCM3052_REG_RIGHT_VOLUME	0x42

/* XXX */
#define onyx_softc i2s_softc

/* XXX */
void kiic_setmode(struct kiic_softc *, u_int, u_int);
int kiic_write(struct device *, int, int, const void *, int);

int onyx_getdev(void *, struct audio_device *);
int onyx_match(struct device *, void *, void *);
void onyx_attach(struct device *, struct device *, void *);
void onyx_defer(struct device *);
void onyx_set_volume(struct onyx_softc *, int, int);
void onyx_get_default_params(void *, int, struct audio_params *);

struct cfattach onyx_ca = {
	sizeof(struct onyx_softc), onyx_match, onyx_attach
};

struct cfdriver onyx_cd = {
	NULL, "onyx", DV_DULL
};

struct audio_hw_if onyx_hw_if = {
	i2s_open,
	i2s_close,
	NULL,
	i2s_query_encoding,
	i2s_set_params,
	i2s_round_blocksize,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	i2s_halt_output,
	i2s_halt_input,
	NULL,
	onyx_getdev,
	NULL,
	i2s_set_port,
	i2s_get_port,
	i2s_query_devinfo,
	i2s_allocm,
	NULL,
	i2s_round_buffersize,
	i2s_mappage,
	i2s_get_props,
	i2s_trigger_output,
	i2s_trigger_input,
	onyx_get_default_params
};

struct audio_device onyx_device = {
	"ONYX",
	"",
	"onyx"
};

int
onyx_match(struct device *parent, void *match, void *aux)
{
	struct confargs *ca = aux;
	int soundbus, soundchip, soundcodec;
	int32_t layout = 0;

	if (strcmp(ca->ca_name, "i2s") != 0)
		return (0);

	if ((soundbus = OF_child(ca->ca_node)) == 0 ||
	    (soundchip = OF_child(soundbus)) == 0)
		return (0);

	if (OF_getprop(soundchip, "platform-onyx-codec-ref",
	    &soundcodec, sizeof soundcodec) == sizeof soundcodec)
		return (1);

	/*
	 * Apple really messed up.  First and second generation iMac
	 * G5 (PowerMac8,1 and PowerMac8,2) have a "deq" i2c device
	 * listed in the OF device tree, which is a telltale sign of
	 * snapper(4).  But in reality that chip isn't there.  So we
	 * match on "layout-id" instead.
	 */
	if (OF_getprop(soundchip, "layout-id", &layout, sizeof layout) &&
	    (layout == 0x2d || layout == 0x56))
		return (1);

	return (0);
}

void
onyx_attach(struct device *parent, struct device *self, void *aux)
{
	struct onyx_softc *sc = (struct onyx_softc *)self;

	sc->sc_setvolume = onyx_set_volume;

	i2s_attach(parent, sc, aux);
	config_defer(self, onyx_defer);
}

void
onyx_defer(struct device *dev)
{
	struct onyx_softc *sc = (struct onyx_softc *)dev;
	struct device *dv;

	TAILQ_FOREACH(dv, &alldevs, dv_list)
		if (strcmp(dv->dv_cfdata->cf_driver->cd_name, "kiic") == 0 &&
		    strcmp(dv->dv_parent->dv_cfdata->cf_driver->cd_name, "macobio") == 0)
			sc->sc_i2c = dv;
	if (sc->sc_i2c == NULL) {
		printf("%s: unable to find i2c\n", sc->sc_dev.dv_xname);
		return;
	}

	/* XXX If i2c has failed to attach, what should we do? */

	audio_attach_mi(&onyx_hw_if, sc, &sc->sc_dev);

	deq_reset(sc);
	onyx_set_volume(sc, 192, 192);
}

int
onyx_getdev(void *h, struct audio_device *retp)
{
	*retp = onyx_device;
	return (0);
}

void
onyx_set_volume(struct onyx_softc *sc, int left, int right)
{
	u_int8_t data;

	sc->sc_vol_l = left;
	sc->sc_vol_r = right;

	kiic_setmode(sc->sc_i2c, I2C_STDSUBMODE, 0);
	data = 128 + (left >> 1);
	kiic_write(sc->sc_i2c, PCM3052_I2C_ADDR,
	    PCM3052_REG_LEFT_VOLUME, &data, 1);
	data = 128 + (right >> 1);
	kiic_write(sc->sc_i2c, PCM3052_I2C_ADDR,
	    PCM3052_REG_RIGHT_VOLUME, &data, 1);
}

void
onyx_get_default_params(void *addr, int mode, struct audio_params *params)
{
	i2s_get_default_params(params);
}


code:
/*	$OpenBSD: openpic.c,v 1.82 2015/06/25 08:56:33 mpi Exp $	*/

/*-
 * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 1995 Per Fogelstrom
 * Copyright (c) 1993, 1994 Charles M. Hannum.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)isa.c	7.2 (Berkeley) 5/12/91
 */

#include "hpb.h"

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

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/intr.h>
#include <machine/psl.h>
#include <machine/pio.h>
#include <dev/ofw/openfirm.h>

#include <macppc/dev/openpicreg.h>

#ifdef OPENPIC_DEBUG
#define DPRINTF(x...)	do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif

#define ICU_LEN 128
int openpic_numirq = ICU_LEN;
#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))

int openpic_pri_share[IPL_NUM];

struct intrq openpic_handler[ICU_LEN];

struct openpic_softc {
	struct device sc_dev;
};

vaddr_t openpic_base;
int	openpic_big_endian;
struct	evcount openpic_spurious;
int	openpic_spurious_irq = 255;

int	openpic_match(struct device *parent, void *cf, void *aux);
void	openpic_attach(struct device *, struct device *, void *);

int	openpic_splraise(int);
int	openpic_spllower(int);
void	openpic_splx(int);

u_int	openpic_read(int reg);
void	openpic_write(int reg, u_int val);

void	openpic_acknowledge_irq(int, int);
void	openpic_enable_irq(int, int, int);
void	openpic_disable_irq(int, int);

void	openpic_calc_mask(void);
void	openpic_set_priority(int, int);
void	*openpic_intr_establish(void *, int, int, int, int (*)(void *), void *,
	    const char *);
void	openpic_intr_disestablish(void *, void *);
void	openpic_collect_preconf_intr(void);
void	openpic_ext_intr(void);
int	openpic_ext_intr_handler(struct intrhand *, int, int *);

/* Generic IRQ management routines. */
void	openpic_gen_acknowledge_irq(int, int);
void	openpic_gen_enable_irq(int, int, int);
void	openpic_gen_disable_irq(int, int);

#if NHPB > 0
/* CPC945 IRQ management routines. */
void	openpic_cpc945_acknowledge_irq(int, int);
void	openpic_cpc945_enable_irq(int, int, int);
void	openpic_cpc945_disable_irq(int, int);
#endif /* NHPB */

struct openpic_ops {
	void	(*acknowledge_irq)(int, int);
	void	(*enable_irq)(int, int, int);
	void	(*disable_irq)(int, int);
} openpic_ops = {
	openpic_gen_acknowledge_irq,
	openpic_gen_enable_irq,
	openpic_gen_disable_irq
};

#ifdef MULTIPROCESSOR
void	openpic_ipi_ddb(void);

/* IRQ vector used for inter-processor interrupts. */
#define IPI_VECTOR_NOP	64
#define IPI_VECTOR_DDB	65

static struct evcount ipi_count;

static int ipi_irq = IPI_VECTOR_NOP;

intr_send_ipi_t openpic_send_ipi;
#endif /* MULTIPROCESSOR */

const struct cfattach openpic_ca = {
	sizeof(struct openpic_softc), openpic_match, openpic_attach
};

struct cfdriver openpic_cd = {
	NULL, "openpic", DV_DULL
};

u_int
openpic_read(int reg)
{
	char *addr = (void *)(openpic_base + reg);

	membar_sync();
	if (openpic_big_endian)
		return in32(addr);
	else
		return in32rb(addr);
}

void
openpic_write(int reg, u_int val)
{
	char *addr = (void *)(openpic_base + reg);

	if (openpic_big_endian)
		out32(addr, val);
	else
		out32rb(addr, val);
	membar_sync();
}

static inline int
openpic_read_irq(int cpu)
{
	return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
}

static inline void
openpic_eoi(int cpu)
{
	openpic_write(OPENPIC_EOI(cpu), 0);
}

int
openpic_match(struct device *parent, void *cf, void *aux)
{
	char type[40];
	int pirq;
	struct confargs *ca = aux;

	bzero (type, sizeof(type));

	if (OF_getprop(ca->ca_node, "interrupt-parent", &pirq, sizeof(pirq))
	    == sizeof(pirq))
		return 0; /* XXX */

	if (strcmp(ca->ca_name, "interrupt-controller") != 0 &&
	    strcmp(ca->ca_name, "mpic") != 0)
		return 0;

	OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
	if (strcmp(type, "open-pic") != 0)
		return 0;

	if (ca->ca_nreg < 8)
		return 0;

	return 1;
}

void
openpic_attach(struct device *parent, struct device *self, void *aux)
{
	struct cpu_info *ci = curcpu();
	struct confargs *ca = aux;
	struct intrq *iq;
	uint32_t reg = 0;
	int i, irq;
	u_int x;

	if (OF_getprop(ca->ca_node, "big-endian", &reg, sizeof reg) == 0)
		openpic_big_endian = 1;

	openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr +
			ca->ca_reg[0], 0x40000);

	/* Reset the PIC */
	x = openpic_read(OPENPIC_CONFIG) | OPENPIC_CONFIG_RESET;
	openpic_write(OPENPIC_CONFIG, x);

	while (openpic_read(OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET)
		delay(100);

	/* openpic may support more than 128 interupts but driver doesn't */
	openpic_numirq = ((openpic_read(OPENPIC_FEATURE) >> 16) & 0x7f)+1;

	printf(": version 0x%x feature %x %s",
	    openpic_read(OPENPIC_VENDOR_ID),
	    openpic_read(OPENPIC_FEATURE),
		openpic_big_endian ? "BE" : "LE" );

	openpic_set_priority(ci->ci_cpuid, 15);

	/* disable all interrupts */
	for (irq = 0; irq < openpic_numirq; irq++)
		openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK);

	for (i = 0; i < openpic_numirq; i++) {
		iq = &openpic_handler[i];
		TAILQ_INIT(&iq->iq_list);
	}

	/* we don't need 8259 pass through mode */
	x = openpic_read(OPENPIC_CONFIG);
	x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE;
	openpic_write(OPENPIC_CONFIG, x);

	/* initialize all vectors to something sane */
	for (irq = 0; irq < ICU_LEN; irq++) {
		x = irq;
		x |= OPENPIC_IMASK;
		x |= OPENPIC_POLARITY_NEGATIVE;
		x |= OPENPIC_SENSE_LEVEL;
		x |= 8 << OPENPIC_PRIORITY_SHIFT;
		openpic_write(OPENPIC_SRC_VECTOR(irq), x);
	}

	/* send all interrupts to cpu 0 */
	for (irq = 0; irq < openpic_numirq; irq++)
		openpic_write(OPENPIC_IDEST(irq), 1 << 0);

	/* clear all pending interrunts */
	for (irq = 0; irq < ICU_LEN; irq++) {
		openpic_read_irq(ci->ci_cpuid);
		openpic_eoi(ci->ci_cpuid);
	}

#ifdef MULTIPROCESSOR
	/* Set up inter-processor interrupts. */
	/* IPI0 - NOP */
	x = IPI_VECTOR_NOP;
	x |= 15 << OPENPIC_PRIORITY_SHIFT;
	openpic_write(OPENPIC_IPI_VECTOR(0), x);
	/* IPI1 - DDB */
	x = IPI_VECTOR_DDB;
	x |= 15 << OPENPIC_PRIORITY_SHIFT;
	openpic_write(OPENPIC_IPI_VECTOR(1), x);

	evcount_attach(&ipi_count, "ipi", &ipi_irq);
#endif

	/* clear all pending interrunts */
	for (irq = 0; irq < ICU_LEN; irq++) {
		openpic_read_irq(0);
		openpic_eoi(0);
	}

#if 0
	openpic_write(OPENPIC_SPURIOUS_VECTOR, 255);
#endif

#if NHPB > 0
	/* Only U4 systems have a big-endian MPIC. */
	if (openpic_big_endian) {
		openpic_ops.acknowledge_irq = openpic_cpc945_acknowledge_irq;
		openpic_ops.enable_irq = openpic_cpc945_enable_irq;
		openpic_ops.disable_irq = openpic_cpc945_disable_irq;
	}
#endif

	install_extint(openpic_ext_intr);

	openpic_set_priority(ci->ci_cpuid, 0);

	intr_establish_func  = openpic_intr_establish;
	intr_disestablish_func = openpic_intr_disestablish;
#ifdef MULTIPROCESSOR
	intr_send_ipi_func = openpic_send_ipi;
#endif

	ppc_smask_init();

	openpic_collect_preconf_intr();

	evcount_attach(&openpic_spurious, "spurious", &openpic_spurious_irq);

	ppc_intr_func.raise = openpic_splraise;
	ppc_intr_func.lower = openpic_spllower;
	ppc_intr_func.x = openpic_splx;

	openpic_set_priority(0, ci->ci_cpl);

	ppc_intr_enable(1);

	printf("\n");
}

/* Must be called with interrupt disable. */
static inline void
openpic_setipl(int newcpl)
{
	struct cpu_info *ci = curcpu();

	ci->ci_cpl = newcpl;
	openpic_set_priority(ci->ci_cpuid, newcpl);
}

int
openpic_splraise(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int ocpl = ci->ci_cpl;
	int s;

	newcpl = openpic_pri_share[newcpl];
	if (ocpl > newcpl)
		newcpl = ocpl;

	s = ppc_intr_disable();
	openpic_setipl(newcpl);
	ppc_intr_enable(s);

	return ocpl;
}

int
openpic_spllower(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int ocpl = ci->ci_cpl;

	openpic_splx(newcpl);

	return ocpl;
}

void
openpic_splx(int newcpl)
{
	struct cpu_info *ci = curcpu();
	int intr, s;

	intr = ppc_intr_disable();
	openpic_setipl(newcpl);
	if (newcpl < IPL_SOFTTTY && (ci->ci_ipending & ppc_smask[newcpl])) {
		s = splsofttty();
		dosoftint(newcpl);
		openpic_setipl(s); /* no-overhead splx */
	}
	ppc_intr_enable(intr);
}

void
openpic_collect_preconf_intr()
{
	int i;
	for (i = 0; i < ppc_configed_intr_cnt; i++) {
		DPRINTF("\n\t%s irq %d level %d fun %p arg %p",
		    ppc_configed_intr[i].ih_what, ppc_configed_intr[i].ih_irq,
		    ppc_configed_intr[i].ih_level, ppc_configed_intr[i].ih_fun,
		    ppc_configed_intr[i].ih_arg);
		openpic_intr_establish(NULL, ppc_configed_intr[i].ih_irq,
		    IST_LEVEL, ppc_configed_intr[i].ih_level,
		    ppc_configed_intr[i].ih_fun, ppc_configed_intr[i].ih_arg,
		    ppc_configed_intr[i].ih_what);
	}
}

/*
 * Register an interrupt handler.
 */
void *
openpic_intr_establish(void *lcv, int irq, int type, int level,
    int (*ih_fun)(void *), void *ih_arg, const char *name)
{
	struct intrhand *ih;
	struct intrq *iq;
	int s, flags;

	if (!LEGAL_IRQ(irq) || type == IST_NONE) {
		printf("%s: bogus irq %d or type %d", __func__, irq, type);
		return (NULL);
	}

	/* no point in sleeping unless someone can free memory. */
	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
	if (ih == NULL)
		panic("%s: can't malloc handler info", __func__);

	iq = &openpic_handler[irq];
	switch (iq->iq_ist) {
	case IST_NONE:
		iq->iq_ist = type;
		break;
	case IST_EDGE:
		intr_shared_edge = 1;
		/* FALLTHROUGH */
	case IST_LEVEL:
		if (type == iq->iq_ist)
			break;
	case IST_PULSE:
		if (type != IST_NONE)
			panic("intr_establish: can't share %s with %s",
			    ppc_intr_typename(iq->iq_ist),
			    ppc_intr_typename(type));
		break;
	}

	flags = level & IPL_MPSAFE;
	level &= ~IPL_MPSAFE;

	KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE);

	ih->ih_fun = ih_fun;
	ih->ih_arg = ih_arg;
	ih->ih_level = level;
	ih->ih_flags = flags;
	ih->ih_irq = irq;

	evcount_attach(&ih->ih_count, name, &ih->ih_irq);

	/*
	 * Append handler to end of list
	 */
	s = ppc_intr_disable();

	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
	openpic_calc_mask();

	ppc_intr_enable(s);

	return (ih);
}

/*
 * Deregister an interrupt handler.
 */
void
openpic_intr_disestablish(void *lcp, void *arg)
{
	struct intrhand *ih = arg;
	int irq = ih->ih_irq;
	struct intrq *iq = &openpic_handler[irq];
	int s;

	if (!LEGAL_IRQ(irq)) {
		printf("%s: bogus irq %d", __func__, irq);
		return;
	}

	/*
	 * Remove the handler from the chain.
	 */
	s = ppc_intr_disable();

	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
	openpic_calc_mask();

	ppc_intr_enable(s);

	evcount_detach(&ih->ih_count);
	free((void *)ih, M_DEVBUF, 0);

	if (TAILQ_EMPTY(&iq->iq_list))
		iq->iq_ist = IST_NONE;
}

/*
 * Recalculate the interrupt masks from scratch.
 * We could code special registry and deregistry versions of this function that
 * would be faster, but the code would be nastier, and we don't expect this to
 * happen very much anyway.
 */

void
openpic_calc_mask()
{
	struct cpu_info *ci = curcpu();
	int irq;
	struct intrhand *ih;
	int i;

	/* disable all openpic interrupts */
	openpic_set_priority(ci->ci_cpuid, 15);

	for (i = IPL_NONE; i < IPL_NUM; i++) {
		openpic_pri_share[i] = i;
	}

	for (irq = 0; irq < openpic_numirq; irq++) {
		int maxipl = IPL_NONE;
		int minipl = IPL_HIGH;
		struct intrq *iq = &openpic_handler[irq];

		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
			if (ih->ih_level > maxipl)
				maxipl = ih->ih_level;
			if (ih->ih_level < minipl)
				minipl = ih->ih_level;
		}

		if (maxipl == IPL_NONE) {
			minipl = IPL_NONE; /* Interrupt not enabled */

			openpic_disable_irq(irq, iq->iq_ist);
		} else {
			for (i = minipl; i <= maxipl; i++) {
				openpic_pri_share[i] = maxipl;
			}
			openpic_enable_irq(irq, iq->iq_ist, maxipl);
		}

		iq->iq_ipl = maxipl;
	}

	/* restore interrupts */
	openpic_set_priority(ci->ci_cpuid, ci->ci_cpl);
}

void
openpic_gen_acknowledge_irq(int irq, int cpuid)
{
	openpic_eoi(cpuid);
}

void
openpic_gen_enable_irq(int irq, int ist, int pri)
{
	u_int x;

	x = irq;

	if (ist == IST_LEVEL)
		x |= OPENPIC_SENSE_LEVEL;
	else
		x |= OPENPIC_SENSE_EDGE;
	x |= OPENPIC_POLARITY_NEGATIVE;
	x |= pri << OPENPIC_PRIORITY_SHIFT;
	openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}

void
openpic_gen_disable_irq(int irq, int ist)
{
	u_int x;

	x = openpic_read(OPENPIC_SRC_VECTOR(irq));
	x |= OPENPIC_IMASK;
	openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}

void
openpic_set_priority(int cpu, int pri)
{
	openpic_write(OPENPIC_CPU_PRIORITY(cpu), pri);
}

int openpic_irqnest[PPC_MAXPROCS];
int openpic_irqloop[PPC_MAXPROCS];

void
openpic_ext_intr(void)
{
	struct cpu_info *ci = curcpu();
	int irq, pcpl, ret;
	int maxipl = IPL_NONE;
	struct intrhand *ih;
	struct intrq *iq;
	int spurious;

	pcpl = ci->ci_cpl;

	openpic_irqloop[ci->ci_cpuid] = 0;
	irq = openpic_read_irq(ci->ci_cpuid);
	openpic_irqnest[ci->ci_cpuid]++;

	while (irq != 255) {
		openpic_irqloop[ci->ci_cpuid]++;
#ifdef OPENPIC_DEBUG
		if (openpic_irqloop[ci->ci_cpuid] > 20 ||
		    openpic_irqnest[ci->ci_cpuid] > 3) {
			printf("irqloop %d irqnest %d\n",
			    openpic_irqloop[ci->ci_cpuid],
			    openpic_irqnest[ci->ci_cpuid]);
		}
#endif
		if (openpic_irqloop[ci->ci_cpuid] > 20) {
			DPRINTF("irqloop %d irqnest %d: returning\n",
			    openpic_irqloop[ci->ci_cpuid],
			    openpic_irqnest[ci->ci_cpuid]);
			openpic_irqnest[ci->ci_cpuid]--;
			return;
		}
#ifdef MULTIPROCESSOR
		if (irq == IPI_VECTOR_NOP || irq == IPI_VECTOR_DDB) {
			ipi_count.ec_count++;
			openpic_eoi(ci->ci_cpuid);
			if (irq == IPI_VECTOR_DDB)
				openpic_ipi_ddb();
			irq = openpic_read_irq(ci->ci_cpuid);
			continue;
		}
#endif
		iq = &openpic_handler[irq];

#ifdef OPENPIC_DEBUG
		if (iq->iq_ipl <= pcpl)
			printf("invalid interrupt %d lvl %d at %d hw %d\n",
			    irq, iq->iq_ipl, pcpl,
			    openpic_read(OPENPIC_CPU_PRIORITY(ci->ci_cpuid)));
#endif

		if (iq->iq_ipl > maxipl)
			maxipl = iq->iq_ipl;
		openpic_splraise(iq->iq_ipl);
		openpic_acknowledge_irq(irq, ci->ci_cpuid);

		spurious = 1;
		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
			ppc_intr_enable(1);
			ret = openpic_ext_intr_handler(ih, pcpl, &spurious);
			(void)ppc_intr_disable();
			if (intr_shared_edge == 00 && ret == 1)
				break;
 		}
		if (spurious) {
			openpic_spurious.ec_count++;
			DPRINTF("spurious intr %d\n", irq);
		}

		uvmexp.intrs++;
		openpic_setipl(pcpl);

		irq = openpic_read_irq(ci->ci_cpuid);
	}

	openpic_splx(pcpl);	/* Process pendings. */
	openpic_irqnest[ci->ci_cpuid]--;
}

int
openpic_ext_intr_handler(struct intrhand *ih, int pcpl, int *spurious)
{
	int ret;
#ifdef MULTIPROCESSOR
	int need_lock;

	if (ih->ih_flags & IPL_MPSAFE)
		need_lock = 0;
	else
		need_lock = pcpl < IPL_SCHED;

	if (need_lock)
		KERNEL_LOCK();
#endif

	ret = (*ih->ih_fun)(ih->ih_arg);
	if (ret) {
		ih->ih_count.ec_count++;
		*spurious = 0;
	}

#ifdef MULTIPROCESSOR
	if (need_lock)
		KERNEL_UNLOCK();
#endif

	return (ret);
}

void
openpic_acknowledge_irq(int irq, int cpuid)
{
	(openpic_ops.acknowledge_irq)(irq, cpuid);
}

void
openpic_enable_irq(int irq, int ist, int pri)
{
	(openpic_ops.enable_irq)(irq, ist, pri);
}

void
openpic_disable_irq(int irq, int ist)
{
	(openpic_ops.disable_irq)(irq, ist);
}

#ifdef MULTIPROCESSOR
void
openpic_send_ipi(struct cpu_info *ci, int id)
{
	switch (id) {
	case PPC_IPI_NOP:
		id = 0;
		break;
	case PPC_IPI_DDB:
		id = 1;
		break;
	default:
		panic("invalid ipi send to cpu %d %d", ci->ci_cpuid, id);
	}

	openpic_write(OPENPIC_IPI(curcpu()->ci_cpuid, id), 1 << ci->ci_cpuid);
}

void
openpic_ipi_ddb(void)
{
#ifdef DDB
	Debugger();
#endif
}
#endif /* MULTIPROCESSOR */

#if NHPB > 0
extern int	hpb_enable_irq(int, int);
extern int	hpb_disable_irq(int, int);
extern void	hpb_eoi(int);

void
openpic_cpc945_acknowledge_irq(int irq, int cpuid)
{
	hpb_eoi(irq);
	openpic_gen_acknowledge_irq(irq, cpuid);
}

void
openpic_cpc945_enable_irq(int irq, int ist, int pri)
{
	if (hpb_enable_irq(irq, ist)) {
		u_int x = irq;

		x |= OPENPIC_SENSE_EDGE;
		x |= OPENPIC_POLARITY_POSITIVE;
		x |= pri << OPENPIC_PRIORITY_SHIFT;
		openpic_write(OPENPIC_SRC_VECTOR(irq), x);

		hpb_eoi(irq);
	} else
		openpic_gen_enable_irq(irq, ist, pri);
}

void
openpic_cpc945_disable_irq(int irq, int ist)
{
	hpb_disable_irq(irq, ist);
	openpic_gen_disable_irq(irq, ist);
}
#endif /* NHPB */


code:
/*	$OpenBSD: openpicreg.h,v 1.2 2007/01/07 13:39:28 miod Exp $	*/
/*	$NetBSD: openpicreg.h,v 1.1 2000/02/14 12:45:53 tsubai Exp $	*/

/*-
 * Copyright (c) 2000 Tsubai Masanari.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * GLOBAL/TIMER register (IDU base + 0x1000)
 */

/* feature reporting reg 0 */
#define OPENPIC_FEATURE			0x1000

/* global config reg 0 */
#define OPENPIC_CONFIG			0x1020
#define  OPENPIC_CONFIG_RESET			0x80000000
#define  OPENPIC_CONFIG_8259_PASSTHRU_DISABLE	0x20000000

/* vendor ID */
#define OPENPIC_VENDOR_ID		0x1080

/* processor initialization reg */
#define OPENPIC_PROC_INIT		0x1090

/* IPI vector/priority reg */
#define OPENPIC_IPI_VECTOR(ipi)		(0x10a0 + (ipi) * 0x10)

/* spurious intr. vector */
#define OPENPIC_SPURIOUS_VECTOR		0x10e0

/*
 * INTERRUPT SOURCE register (IDU base + 0x10000)
 */

/* interrupt vector/priority reg */
#define OPENPIC_SRC_VECTOR(irq)		(0x10000 + (irq) * 0x20)
#define  OPENPIC_SENSE_LEVEL			0x00400000
#define  OPENPIC_SENSE_EDGE			0x00000000
#define  OPENPIC_POLARITY_POSITIVE		0x00800000
#define  OPENPIC_POLARITY_NEGATIVE		0x00000000
#define  OPENPIC_IMASK				0x80000000
#define  OPENPIC_ACTIVITY			0x40000000
#define  OPENPIC_PRIORITY_MASK			0x000f0000
#define  OPENPIC_PRIORITY_SHIFT			16
#define  OPENPIC_VECTOR_MASK			0x000000ff

/* interrupt destination cpu */
#define OPENPIC_IDEST(irq)		(0x10010 + (irq) * 0x20)

/*
 * PROCESSOR register (IDU base + 0x20000)
 */

/* IPI command reg */
#define OPENPIC_IPI(cpu, ipi)		(0x20040 + (cpu) * 0x1000 + (ipi) * 0x10)

/* current task priority reg */
#define OPENPIC_CPU_PRIORITY(cpu)	(0x20080 + (cpu) * 0x1000)
#define  OPENPIC_CPU_PRIORITY_MASK		0x0000000f

/* interrupt acknowledge reg */
#define OPENPIC_IACK(cpu)		(0x200a0 + (cpu) * 0x1000)

/* end of interrupt reg */
#define OPENPIC_EOI(cpu)		(0x200b0 + (cpu) * 0x1000)


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

/*
 * This driver allows entering DDB by pushing the "Programmers Switch",
 * which can be found on many "Old World" and some early "New World" MacPPC.
 */

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

#include <ddb/db_var.h>
#include <dev/ofw/openfirm.h>

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

struct pgs_softc {
	struct device	sc_dev;
	int		sc_node;
	int 		sc_intr;
};

void	pgs_attach(struct device *, struct device *, void *);
int	pgs_match(struct device *, void *, void *);
int	pgs_intr(void *);

struct cfattach pgs_ca = {
	sizeof(struct pgs_softc), pgs_match, pgs_attach
};

struct cfdriver pgs_cd = {
	NULL, "pgs", DV_DULL
};

int
pgs_match(struct device *parent, void *arg, void *aux)
{
	struct confargs *ca = aux;
	char type[32];

	if (strcmp(ca->ca_name, "programmer-switch") != 0)
		return 0;

	OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
	if (strcmp(type, "programmer-switch") != 0)
		return 0;

	return 1;
}

void
pgs_attach(struct device *parent, struct device *self, void *aux)
{
	struct pgs_softc *sc = (struct pgs_softc *)self;
	struct confargs *ca = aux;
	int intr[2];

	sc->sc_node = ca->ca_node;

	OF_getprop(sc->sc_node, "interrupts", intr, sizeof(intr));
	sc->sc_intr = intr[0];

	printf(": irq %d\n", sc->sc_intr);

	mac_intr_establish(parent, sc->sc_intr, IST_LEVEL,
	    IPL_HIGH, pgs_intr, sc, sc->sc_dev.dv_xname);
}

int
pgs_intr(void *v)
{
#ifdef DDB
	if (db_console)
		Debugger();
#else
	printf("programmer-switch pressed, debugger not available.\n");
#endif

	return 1;
}


code:
/*	$OpenBSD: piic.c,v 1.3 2011/06/16 10:44:33 mpi Exp $	*/

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

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

#include <machine/autoconf.h>

#include <dev/i2c/i2cvar.h>

#include <arch/macppc/dev/maci2cvar.h>
#include <arch/macppc/dev/pm_direct.h>

struct piic_softc {
	struct device	sc_dev;

	struct rwlock	sc_buslock;
	struct i2c_controller sc_i2c_tag;
};

int     piic_match(struct device *, void *, void *);
void    piic_attach(struct device *, struct device *, void *);

struct cfattach piic_ca = {
	sizeof(struct piic_softc), piic_match, piic_attach
};

struct cfdriver piic_cd = {
        NULL, "piic", DV_DULL,
};

int	piic_i2c_acquire_bus(void *, int);
void	piic_i2c_release_bus(void *, int);
int	piic_i2c_exec(void *, i2c_op_t, i2c_addr_t,
	    const void *, size_t, void *buf, size_t, int);

int
piic_match(struct device *parent, void *cf, void *aux)
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "piic") != 0)
		return (0);

	return (1);
}

void
piic_attach(struct device *parent, struct device *self, void *aux)
{
	struct piic_softc *sc = (struct piic_softc *)self;
	struct confargs *ca = aux;
	struct i2cbus_attach_args iba;

	printf("\n");

	rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);

	sc->sc_i2c_tag.ic_cookie = sc;
	sc->sc_i2c_tag.ic_acquire_bus = piic_i2c_acquire_bus;
	sc->sc_i2c_tag.ic_release_bus = piic_i2c_release_bus;
	sc->sc_i2c_tag.ic_exec = piic_i2c_exec;

	bzero(&iba, sizeof iba);
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_i2c_tag;
	iba.iba_bus_scan = maciic_scan;
	iba.iba_bus_scan_arg = &ca->ca_node;
	config_found(&sc->sc_dev, &iba, NULL);
}

int
piic_i2c_acquire_bus(void *cookie, int flags)
{
	struct piic_softc *sc = cookie;

	return (rw_enter(&sc->sc_buslock, RW_WRITE));
}

void
piic_i2c_release_bus(void *cookie, int flags)
{
	struct piic_softc *sc = cookie;

	rw_exit(&sc->sc_buslock);
}

int
piic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	u_int8_t pmu_op = PMU_I2C_NORMAL;
	int retries = 10;
	PMData p;

	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 5)
		return (EINVAL);

	if (cmdlen == 0)
		pmu_op = PMU_I2C_SIMPLE;
	else if (I2C_OP_READ_P(op))
		pmu_op = PMU_I2C_COMBINED;

	p.command = PMU_I2C;
	p.num_data = 7 + len;
	p.s_buf = p.r_buf = p.data;

	p.data[0] = addr >> 7;	/* bus number */
	p.data[1] = pmu_op;
	p.data[2] = 0;
	p.data[3] = addr << 1;
	p.data[4] = *(u_int8_t *)cmdbuf;
	p.data[5] = addr << 1 | I2C_OP_READ_P(op);
	p.data[6] = len;
	memcpy(&p.data[7], buf, len);

	if (pmgrop(&p))
		return (EIO);

	while (retries--) {
		p.command = PMU_I2C;
		p.num_data = 1;
		p.s_buf = p.r_buf = p.data;
		p.data[0] = 0;

		if (pmgrop(&p))
			return (EIO);

		if (p.data[0] == 1)
			break;

		DELAY(10 * 1000);
	}

	if (I2C_OP_READ_P(op))
		memcpy(buf, &p.data[1], len);
	return (0);
}