code:
/*	$OpenBSD: pcf8591_ofw.c,v 1.5 2007/10/22 20:53:10 cnst Exp $ */

/*
 * Copyright (c) 2006 Damien Miller <djm@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.
 */

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

#include <dev/ofw/openfirm.h>
#include <dev/i2c/i2cvar.h>

#define PCF8591_CHANNELS	4

struct pcfadc_channel {
	u_int		chan_num;
	struct ksensor	chan_sensor;
};

struct pcfadc_softc {
	struct device		sc_dev;
	i2c_tag_t		sc_tag;
	i2c_addr_t		sc_addr;
	u_char			sc_xlate[256];
	u_int			sc_nchan;
	struct pcfadc_channel	sc_channels[PCF8591_CHANNELS];
	struct ksensordev	sc_sensordev;
};

int	pcfadc_match(struct device *, void *, void *);
void	pcfadc_attach(struct device *, struct device *, void *);
void	pcfadc_refresh(void *);

struct cfattach pcfadc_ca = {
	sizeof(struct pcfadc_softc), pcfadc_match, pcfadc_attach
};

struct cfdriver pcfadc_cd = {
	NULL, "pcfadc", DV_DULL
};

int
pcfadc_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "i2cpcf,8591") != 0)
		return (0);

	return (1);
}

void
pcfadc_attach(struct device *parent, struct device *self, void *aux)
{
	struct pcfadc_softc *sc = (struct pcfadc_softc *)self;
	u_char chanuse[PCF8591_CHANNELS * 4], desc[PCF8591_CHANNELS * 32];
	u_char *cp;
	u_int8_t junk[PCF8591_CHANNELS + 1];
	u_int32_t transinfo[PCF8591_CHANNELS * 4];
	struct i2c_attach_args *ia = aux;
	int dlen, clen, tlen, node = *(int *)ia->ia_cookie;
	u_int i;

	if ((dlen = OF_getprop(node, "channels-description", desc,
	    sizeof(desc))) < 0) {
		printf(": couldn't find \"channels-description\" property\n");
		return;
	}
	if (dlen > sizeof(desc) || desc[dlen - 1] != '\0') {
		printf(": bad \"channels-description\" property\n");
		return;
	}
	if ((clen = OF_getprop(node, "channels-in-use", chanuse,
	    sizeof(chanuse))) < 0) {
		printf(": couldn't find \"channels-in-use\" property\n");
		return;
	}
	if ((clen % 4) != 0) {
		printf(": invalid \"channels-in-use\" length %d\n", clen);
		return;
	}
	sc->sc_nchan = clen / 4;
	if (sc->sc_nchan > PCF8591_CHANNELS) {
		printf(": invalid number of channels (%d)\n", sc->sc_nchan);
		return;
	}

	if ((tlen = OF_getprop(node, "tables", sc->sc_xlate,
	    sizeof(sc->sc_xlate))) < 0) {
		printf(": couldn't find \"tables\" property\n");
		return;
	}
	/* We only support complete, single width tables */
	if (tlen != 256) {
		printf(": invalid \"tables\" length %d\n", tlen);
		return;
	}

	if ((tlen = OF_getprop(node, "translation", transinfo,
	    sizeof(transinfo))) < 0) {
		printf(": couldn't find \"translation\" property\n");
		return;
	}
	if (tlen != (sc->sc_nchan * 4 * 4)) {
		printf(": invalid \"translation\" length %d\n", tlen);
		return;
	}

	cp = desc;
	for (i = 0; i < sc->sc_nchan; i++) {
		struct pcfadc_channel *chp = &sc->sc_channels[i];

		chp->chan_sensor.type = SENSOR_TEMP;

		if (cp >= desc + dlen) {
			printf(": invalid \"channels-description\"\n");
			return;
		}
		strlcpy(chp->chan_sensor.desc, cp,
		    sizeof(chp->chan_sensor.desc));
		cp += strlen(cp) + 1;

		/*
		 * We only support input temperature channels, with
		 * valid channel numbers, and basic (unscaled) translation
		 *
		 * XXX TODO: support voltage (type 2) channels and type 4
		 * (scaled) translation tables
		 */
		if (chanuse[(i * 4)] > PCF8591_CHANNELS || /* channel # */
		    chanuse[(i * 4) + 1] != 0 ||	/* dir == input */
		    chanuse[(i * 4) + 2] != 1 ||	/* type == temp */
		    transinfo[(i * 4)] != 3 ||		/* xlate == table */
		    transinfo[(i * 4) + 2] != 0 ||	/* no xlate offset */
		    transinfo[(i * 4) + 3] != 0x100) {	/* xlate tbl length */
			printf(": unsupported sensor %d\n", i);
			return;
		}
		chp->chan_num = chanuse[(i * 4)];
	}

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	/* Try a read now, so we can fail if it doesn't work */
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    NULL, 0, junk, sc->sc_nchan + 1, 0)) {
		printf(": read failed\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	for (i = 0; i < sc->sc_nchan; i++)
		sensor_attach(&sc->sc_sensordev,
		    &sc->sc_channels[i].chan_sensor);

	if (sensor_task_register(sc, pcfadc_refresh, 5) == NULL) {
		printf(": unable to register update task\n");
		return;
	}

	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
pcfadc_refresh(void *arg)
{
	struct pcfadc_softc *sc = arg;
	u_int i;
	u_int8_t data[PCF8591_CHANNELS + 1];

	iic_acquire_bus(sc->sc_tag, 0);
	/* NB: first byte out is stale, so read num_channels + 1 */
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    NULL, 0, data, PCF8591_CHANNELS + 1, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	iic_release_bus(sc->sc_tag, 0);

	/* XXX: so far this only supports temperature channels */
	for (i = 0; i < sc->sc_nchan; i++) {
		struct pcfadc_channel *chp = &sc->sc_channels[i];

		chp->chan_sensor.value = 273150000 + 1000000 *
		    sc->sc_xlate[data[1 + chp->chan_num]];
	}
}


code:
/*	$OpenBSD: pcfiic_ebus.c,v 1.13 2008/06/08 03:07:40 deraadt Exp $ */

/*
 * Copyright (c) 2006 David Gwynne <dlg@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.
 */

/*
 * Device specific driver for the EBus i2c devices found on some sun4u
 * systems. On systems not having a boot-bus controller the i2c devices
 * are PCF8584.
 */

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

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

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

#include <dev/i2c/i2cvar.h>
#include <sparc64/dev/ofwi2cvar.h>

#include <dev/ic/pcf8584var.h>

int	pcfiic_ebus_match(struct device *, void *, void *);
void	pcfiic_ebus_attach(struct device *, struct device *, void *);

struct pcfiic_ebus_softc {
	struct pcfiic_softc	esc_sc;

	int			esc_node;
	void			*esc_ih;
};

struct cfattach pcfiic_ebus_ca = {
	sizeof(struct pcfiic_ebus_softc), pcfiic_ebus_match, pcfiic_ebus_attach
};

void	envctrl_scan(struct device *, struct i2cbus_attach_args *, void *);
void	envctrltwo_scan(struct device *, struct i2cbus_attach_args *, void *);

int
pcfiic_ebus_match(struct device *parent, void *match, void *aux)
{
	struct ebus_attach_args		*ea = aux;
	char				compat[32];

	if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 ||
	    strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
		return (1);

	if (strcmp(ea->ea_name, "i2c") != 0)
		return (0);

	if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1)
		return (0);

	if (strcmp(compat, "pcf8584") == 0 ||
	    strcmp(compat, "i2cpcf,8584") == 0 ||
	    strcmp(compat, "SUNW,i2c-pic16f747") == 0 ||
	    strcmp(compat, "SUNW,bbc-i2c") == 0)
		return (1);

	return (0);
}

void
pcfiic_ebus_attach(struct device *parent, struct device *self, void *aux)
{
	struct pcfiic_ebus_softc	*esc = (struct pcfiic_ebus_softc *)self;
	struct pcfiic_softc		*sc = &esc->esc_sc;
	struct ebus_attach_args		*ea = aux;
	char				compat[32];
	u_int64_t			addr;
	u_int8_t			clock = PCF_CLOCK_12 | PCF_FREQ_90;
	int				swapregs = 0;

	if (ea->ea_nregs < 1 || ea->ea_nregs > 2) {
		printf(": expected 1 or 2 registers, got %d\n", ea->ea_nregs);
		return;
	}

	if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 &&
	    strcmp(compat, "SUNW,bbc-i2c") == 0) {
		/*
		 * On BBC-based machines, Sun swapped the order of
		 * the registers on their clone pcf, plus they feed
		 * it a non-standard clock.
		 */
		int clk = getpropint(findroot(), "clock-frequency", 0);

		if (clk < 105000000)
			clock = PCF_CLOCK_3 | PCF_FREQ_90;
		else if (clk < 160000000)
			clock = PCF_CLOCK_4_43 | PCF_FREQ_90;
		swapregs = 1;
	}

	if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) {
		addr = 0xaa;
	} else if (addr == 0x00 || addr > 0xff) {
		printf(": invalid address on I2C bus");
		return;
	}

	/* Prefer prom mapping, then memory mapping, then io mapping */
	if (ea->ea_nvaddrs) {
		if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
		    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) != 0)
			goto fail;
		sc->sc_iot = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else {
fail:
		printf(": can't map register space\n");
               	return;
	}

	if (ea->ea_nregs == 2) {
		/*
		 * Second register only occurs on BBC-based machines,
		 * and is likely not prom mapped
		*/
		if (ebus_bus_map(sc->sc_iot, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[1]),
		    ea->ea_regs[1].size, 0, 0, &sc->sc_ioh2) != 0) {
			printf(": can't map 2nd register space\n");
			return;
		}
		sc->sc_master = 1;
	}

	if (ea->ea_nintrs >= 1)
		esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
		    IPL_BIO, 0, pcfiic_intr, sc, self->dv_xname);
	else
		esc->esc_ih = NULL;

	if (esc->esc_ih == NULL)
		sc->sc_poll = 1;

	if (strcmp(ea->ea_name, "SUNW,envctrl") == 0)
		pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0,
		    envctrl_scan, &ea->ea_node);
	else if (strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
		pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0,
		    envctrltwo_scan, &ea->ea_node);
	else
		pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock, swapregs,
		    ofwiic_scan, &ea->ea_node);
}

void
envctrl_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
{
	extern int iic_print(void *, const char *);
	struct i2c_attach_args ia;

	memset(&ia, 0, sizeof(ia));
	ia.ia_tag = iba->iba_tag;
	ia.ia_cookie = aux;

	/* Power supply 1 temperature. */
	ia.ia_addr = 0x48;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);

	/* Power supply 2 termperature. */
	ia.ia_addr = 0x49;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);

	/* Power supply 3 tempterature. */
	ia.ia_addr = 0x4a;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);

	/* Ambient tempterature. */
	ia.ia_addr = 0x4d;
	ia.ia_name = "lm75";
	config_found(self, &ia, iic_print);

	/* CPU temperatures. */
	ia.ia_addr = 0x4f;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);
}

void
envctrltwo_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
{
	extern int iic_print(void *, const char *);
	struct i2c_attach_args ia;

	memset(&ia, 0, sizeof(ia));
	ia.ia_tag = iba->iba_tag;
	ia.ia_cookie = aux;

	ia.ia_addr = 0x4a;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);

	ia.ia_addr = 0x4f;
	ia.ia_name = "ecadc";
	config_found(self, &ia, iic_print);
}

license: unknown


code:
/*	$OpenBSD: pci_machdep.c,v 1.44 2014/05/10 12:15:19 kettenis Exp $	*/
/*	$NetBSD: pci_machdep.c,v 1.22 2001/07/20 00:07:13 eeh Exp $	*/

/*
 * Copyright (c) 1999, 2000 Matthew R. Green
 * 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.
 */

/*
 * functions expected by the MI PCI code.
 */

#ifdef DEBUG
#define SPDB_CONF	0x01
#define SPDB_INTR	0x04
#define SPDB_INTMAP	0x08
#define SPDB_PROBE	0x20
int sparc_pci_debug = 0x0;
#define DPRINTF(l, s)	do { if (sparc_pci_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)	do { } while (0)
#endif

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

#define _SPARC_BUS_DMA_PRIVATE
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>

#include <dev/ofw/ofw_pci.h>

#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/iommuvar.h>
#include <sparc64/dev/psychoreg.h>
#include <sparc64/dev/psychovar.h>
#include <sparc64/sparc64/cache.h>

/* this is a base to be copied */
struct sparc_pci_chipset _sparc_pci_chipset = {
	NULL,
};

static int pci_bus_frequency(int node);

/*
 * functions provided to the MI code.
 */

void
pci_attach_hook(parent, self, pba)
	struct device *parent;
	struct device *self;
	struct pcibus_attach_args *pba;
{
	/* Don't do anything */
}

int
pci_bus_maxdevs(pc, busno)
	pci_chipset_tag_t pc;
	int busno;
{

	return 32;
}

pcitag_t
pci_make_tag(pc, b, d, f)
	pci_chipset_tag_t pc;
	int b;
	int d;
	int f;
{
	struct ofw_pci_register reg;
	pcitag_t tag;
	int busrange[2];
	int node, len;
#ifdef DEBUG
	char name[80];
	bzero(name, sizeof(name));
#endif

	if (pc->busnode[b])
		return PCITAG_CREATE(0, b, d, f);

	/*
	 * Hunt for the node that corresponds to this device
	 *
	 * We could cache this info in an array in the parent
	 * device... except then we have problems with devices
	 * attached below pci-pci bridges, and we would need to
	 * add special code to the pci-pci bridge to cache this
	 * info.
	 */

	tag = PCITAG_CREATE(-1, b, d, f);
	node = pc->rootnode;

	/*
	 * Traverse all peers until we find the node or we find
	 * the right bridge.
	 */
	for (node = ((node)); node; node = OF_peer(node)) {

#ifdef DEBUG
		if (sparc_pci_debug & SPDB_PROBE) {
			OF_getprop(node, "name", &name, sizeof(name));
			printf("checking node %x %s\n", node, name);
		}
#endif

		/*
		 * Check for PCI-PCI bridges.  If the device we want is
		 * in the bus-range for that bridge, work our way down.
		 */
		while ((OF_getprop(node, "bus-range", (void *)&busrange,
			sizeof(busrange)) == sizeof(busrange)) &&
			(b >= busrange[0] && b <= busrange[1])) {
			/* Go down 1 level */
			node = OF_child(node);
#ifdef DEBUG
			if (sparc_pci_debug & SPDB_PROBE) {
				OF_getprop(node, "name", &name, sizeof(name));
				printf("going down to node %x %s\n",
					node, name);
			}
#endif
		}

		/*
		 * We only really need the first `reg' property.
		 *
		 * For simplicity, we'll query the `reg' when we
		 * need it.  Otherwise we could malloc() it, but
		 * that gets more complicated.
		 */
		len = OF_getproplen(node, "reg");
		if (len < sizeof(reg))
			continue;
		if (OF_getprop(node, "reg", (void *)&reg, sizeof(reg)) != len)
			panic("pci_probe_bus: OF_getprop len botch");

		if (b != OFW_PCI_PHYS_HI_BUS(reg.phys_hi))
			continue;
		if (d != OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi))
			continue;
		if (f != OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi))
			continue;

		/* Got a match */
		tag = PCITAG_CREATE(node, b, d, f);

		return (tag);
	}
	/* No device found -- return a dead tag */
	return (tag);
}

void
pci_decompose_tag(pc, tag, bp, dp, fp)
	pci_chipset_tag_t pc;
	pcitag_t tag;
	int *bp, *dp, *fp;
{

	if (bp != NULL)
		*bp = PCITAG_BUS(tag);
	if (dp != NULL)
		*dp = PCITAG_DEV(tag);
	if (fp != NULL)
		*fp = PCITAG_FUN(tag);
}

static int
pci_bus_frequency(int node)
{
	int len, bus_frequency;

	len = OF_getproplen(node, "clock-frequency");
	if (len < sizeof(bus_frequency)) {
		DPRINTF(SPDB_PROBE,
		    ("pci_bus_frequency: clock-frequency len %d too small\n",
		     len));
		return 33;
	}
	if (OF_getprop(node, "clock-frequency", &bus_frequency,
		       sizeof(bus_frequency)) != len) {
		DPRINTF(SPDB_PROBE,
		    ("pci_bus_frequency: could not read clock-frequency\n"));
		return 33;
	}
	return bus_frequency / 1000000;
}

int
sparc64_pci_enumerate_bus(struct pci_softc *sc,
    int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
{
	struct ofw_pci_register reg;
	pci_chipset_tag_t pc = sc->sc_pc;
	pcitag_t tag;
	pcireg_t class, csr, bhlc, ic;
	int node, b, d, f, ret;
	int bus_frequency, lt, cl, cacheline;
	char name[30];

	if (sc->sc_bridgetag)
		node = PCITAG_NODE(*sc->sc_bridgetag);
	else
		node = pc->rootnode;

	bus_frequency = pci_bus_frequency(node);

	/*
	 * Make sure the cache line size is at least as big as the
	 * ecache line and the streaming cache (64 byte).
	 */
	cacheline = max(cacheinfo.ec_linesize, 64);

	for (node = OF_child(node); node != 0 && node != -1;
	     node = OF_peer(node)) {
		if (!checkstatus(node))
			continue;

		name[0] = name[29] = 0;
		OF_getprop(node, "name", name, sizeof(name));

		if (OF_getprop(node, "class-code", &class, sizeof(class)) !=
		    sizeof(class))
			continue;
		if (OF_getprop(node, "reg", &reg, sizeof(reg)) < sizeof(reg))
			panic("pci_enumerate_bus: \"%s\" regs too small", name);

		b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
		d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
		f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);

		if (sc->sc_bus != b) {
			printf("%s: WARNING: incorrect bus # for \"%s\" "
			"(%d/%d/%d)\n", sc->sc_dev.dv_xname, name, b, d, f);
			continue;
		}

		tag = PCITAG_CREATE(node, b, d, f);

		/*
		 * Turn on parity and fast-back-to-back for the device.
		 */
		csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
		if (csr & PCI_STATUS_BACKTOBACK_SUPPORT)
			csr |= PCI_COMMAND_BACKTOBACK_ENABLE;
		csr |= PCI_COMMAND_PARITY_ENABLE;
		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);

		/*
		 * Initialize the latency timer register for busmaster
		 * devices to work properly.
		 *   latency-timer = min-grant * bus-freq / 4  (from FreeBSD)
		 * Also initialize the cache line size register.
		 * Solaris anytime sets this register to the value 0x10.
		 */
		bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
		ic = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);

		lt = min(PCI_MIN_GNT(ic) * bus_frequency / 4, 255);
		if (lt == 0 || lt < PCI_LATTIMER(bhlc))
			lt = PCI_LATTIMER(bhlc);

		cl = PCI_CACHELINE(bhlc);
		if (cl == 0)
			cl = cacheline;

		bhlc &= ~((PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT) |
			  (PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT));
		bhlc |= (lt << PCI_LATTIMER_SHIFT) |
			(cl << PCI_CACHELINE_SHIFT);
		pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc);

		ret = pci_probe_device(sc, tag, match, pap);
		if (match != NULL && ret != 0)
			return (ret);
	}

	return (0);
}

int
pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
	int val = 0;

        if (PCITAG_NODE(tag) != -1)
		val = pc->conf_size(pc, tag);

        return (val);
}

pcireg_t
pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
        pcireg_t val = (pcireg_t)~0;

        if (PCITAG_NODE(tag) != -1)
		val = pc->conf_read(pc, tag, reg);

        return (val);
}

void
pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
        if (PCITAG_NODE(tag) != -1)
		pc->conf_write(pc, tag, reg, data);
}

/*
 * interrupt mapping foo.
 * XXX: how does this deal with multiple interrupts for a device?
 */
int
pci_intr_map(pa, ihp)
	struct pci_attach_args *pa;
	pci_intr_handle_t *ihp;
{
	pcitag_t tag = pa->pa_tag;
	int interrupts[4], ninterrupts;
	int len, node = PCITAG_NODE(tag);
	char devtype[30];

	len = OF_getproplen(node, "interrupts");
	if (len < 0 || len < sizeof(interrupts[0])) {
		DPRINTF(SPDB_INTMAP,
			("pci_intr_map: interrupts len %d too small\n", len));
		return (ENODEV);
	}
	if (OF_getprop(node, "interrupts", interrupts,
	    sizeof(interrupts)) != len) {
		DPRINTF(SPDB_INTMAP,
			("pci_intr_map: could not read interrupts\n"));
		return (ENODEV);
	}

	/*
	 * If we have multiple interrupts for a device, choose the one
	 * that corresponds to the PCI function.  This makes the
	 * second PC Card slot on the UltraBook get the right interrupt.
	 */
	ninterrupts = len / sizeof(interrupts[0]);
	if (PCITAG_FUN(pa->pa_tag) < ninterrupts)
		interrupts[0] = interrupts[PCITAG_FUN(pa->pa_tag)];

	if (OF_mapintr(node, &interrupts[0], sizeof(interrupts[0]),
	    sizeof(interrupts)) < 0) {
		interrupts[0] = -1;
	}
	/* Try to find an IPL for this type of device. */
	if (OF_getprop(node, "device_type", &devtype, sizeof(devtype)) > 0) {
		for (len = 0;  intrmap[len].in_class; len++)
			if (strcmp(intrmap[len].in_class, devtype) == 0) {
				interrupts[0] |= INTLEVENCODE(intrmap[len].in_lev);
				break;
			}
	}

	/* XXXX -- we use the ino.  What if there is a valid IGN? */
	*ihp = interrupts[0];

	if (pa->pa_pc->intr_map)
		return ((*pa->pa_pc->intr_map)(pa, ihp));
	else
		return (0);
}

int
pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	pci_chipset_tag_t pc = pa->pa_pc;
	pcitag_t tag = pa->pa_tag;

	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
	    pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
		return (-1);

	*ihp = PCITAG_OFFSET(pa->pa_tag) | PCI_INTR_MSI;
	return (0);
}

int
pci_intr_line(pci_chipset_tag_t pc, pci_intr_handle_t ih)
{
	return (ih);
}

const char *
pci_intr_string(pc, ih)
	pci_chipset_tag_t pc;
	pci_intr_handle_t ih;
{
	static char str[16];

	DPRINTF(SPDB_INTR, ("pci_intr_string: ih %u", ih));
	if (ih & PCI_INTR_MSI)
		snprintf(str, sizeof str, "msi");
	else
		snprintf(str, sizeof str, "ivec 0x%llx", INTVEC(ih));
	DPRINTF(SPDB_INTR, ("; returning %s\n", str));

	return (str);
}

void *
pci_intr_establish(pc, ih, level, func, arg, what)
	pci_chipset_tag_t pc;
	pci_intr_handle_t ih;
	int level;
	int (*func)(void *);
	void *arg;
	const char *what;
{
	void *cookie;
	int flags = 0;

	if (level & IPL_MPSAFE) {
		flags |= BUS_INTR_ESTABLISH_MPSAFE;
		level &= ~IPL_MPSAFE;
	}

	DPRINTF(SPDB_INTR, ("pci_intr_establish: ih %lu; level %d",
	    (u_long)ih, level));
	cookie = bus_intr_establish(pc->bustag, ih, level, flags,
	    func, arg, what);

	DPRINTF(SPDB_INTR, ("; returning handle %p\n", cookie));
	return (cookie);
}

void
pci_intr_disestablish(pc, cookie)
	pci_chipset_tag_t pc;
	void *cookie;
{

	DPRINTF(SPDB_INTR, ("pci_intr_disestablish: cookie %p\n", cookie));

	/* XXX */
	printf("can't disestablish PCI interrupts yet\n");
}

void
pci_msi_enable(pci_chipset_tag_t pc, pcitag_t tag, bus_addr_t addr, int vec)
{
	pcireg_t reg;
	int off;

	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
		panic("%s: no msi capability", __func__);

	if (reg & PCI_MSI_MC_C64) {
		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
		pci_conf_write(pc, tag, off + PCI_MSI_MAU32, 0);
		pci_conf_write(pc, tag, off + PCI_MSI_MD64, vec);
	} else {
		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
		pci_conf_write(pc, tag, off + PCI_MSI_MD32, vec);
	}
	pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
}


code:
/*	$OpenBSD: pckbc_ebus.c,v 1.13 2015/05/24 10:57:47 miod Exp $	*/

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

/*
 * Driver for i8042 keyboard controller found on some PCI based
 * UltraSPARCs
 *
 * Effort sponsored in part by the Defense Advanced Research Projects
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
 *
 */

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

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

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

#include <dev/ic/i8042reg.h>
#include <dev/ic/pckbcvar.h>

struct pckbc_ebus_softc {
	struct pckbc_softc sc_pckbc;
	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_ioh;
	bus_space_handle_t sc_ioh_c;
	bus_space_handle_t sc_ioh_d;
	void *sc_irq[2];
	int sc_node;
};

int pckbc_ebus_match(struct device *, void *, void *);
void pckbc_ebus_attach(struct device *, struct device *, void *);

struct cfattach pckbc_ebus_ca = {
	sizeof(struct pckbc_ebus_softc), pckbc_ebus_match, pckbc_ebus_attach
};

int pckbc_ebus_is_console(struct pckbc_ebus_softc *);

int
pckbc_ebus_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct ebus_attach_args *ea = aux;

	if (strcmp(ea->ea_name, "8042") == 0)
		return (1);
	return (0);
}

void
pckbc_ebus_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct pckbc_ebus_softc *sc = (void *)self;
	struct pckbc_softc *psc = &sc->sc_pckbc;
	struct ebus_attach_args *ea = aux;
	struct pckbc_internal *t = NULL;
	int console;
	int flags = 0;

	sc->sc_node = ea->ea_node;
	console = pckbc_ebus_is_console(sc);

	/* Use prom address if available, otherwise map it. */
	if (ea->ea_nvaddrs && bus_space_map(ea->ea_iotag, ea->ea_vaddrs[0], 0,
	    0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
	    0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
	    0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf(": can't map register space\n");
		return;
	}

	/*
	 * The 8042 controller found on the Tadpole SPARCLE doesn't
	 * implement XT scan code translation.
	 * XXX I have not checked the value of the model property on
	 * XXX UltraAXe boards...
	 */
	{
		char model[128];
		OF_getprop(ea->ea_node, "model", &model, sizeof model);
		if (strcmp(model, "INTC,80c42") == 0)
			flags = PCKBC_CANT_TRANSLATE | PCKBC_NEED_AUXWRITE;
	}

	if (console) {
		if (pckbc_cnattach(sc->sc_iot,
		    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), KBCMDP, flags) == 0) {
			t = &pckbc_consdata;
			pckbc_console_attached = 1;
			sc->sc_ioh_c = t->t_ioh_c;
			sc->sc_ioh_d = t->t_ioh_d;
		} else
			console = 0;
	}

	if (console == 0) {
		if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
		    KBCMDP, sizeof(u_int32_t), &sc->sc_ioh_c) != 0) {
			printf(": couldn't get cmd subregion\n");
			return;
		}
		if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
		    KBDATAP, sizeof(u_int32_t), &sc->sc_ioh_d) != 0) {
			printf(": couldn't get data subregion\n");
			return;
		}

		t = malloc(sizeof(*t), M_DEVBUF, M_NOWAIT | M_ZERO);
		t->t_flags = flags;
	}

	sc->sc_irq[0] = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
	    IPL_TTY, 0, pckbcintr, psc, self->dv_xname);
	if (sc->sc_irq[0] == NULL) {
		printf(": couldn't get intr0\n");
		return;
	}

	sc->sc_irq[1] = bus_intr_establish(sc->sc_iot, ea->ea_intrs[1],
	    IPL_TTY, 0, pckbcintr, psc, self->dv_xname);
	if (sc->sc_irq[1] == NULL) {
		printf(": couldn't get intr1\n");
		return;
	}

	t->t_iot = sc->sc_iot;
	t->t_ioh_c = sc->sc_ioh_c;
	t->t_ioh_d = sc->sc_ioh_d;
	t->t_cmdbyte = KC8_CPU;
	t->t_sc = psc;

	psc->id = t;

	printf("\n");
	pckbc_attach(psc, 0);
}

int
pckbc_ebus_is_console(sc)
	struct pckbc_ebus_softc *sc;
{
	char *name;
	int node;

	/*
	 * Loop through the children of 8042 and see if the keyboard
	 * exists, and further, whether it is the console input device.
	 * This is almost redundant because 8042 doesn't show up in
	 * device tree unless a keyboard is in fact attached.
	 */
	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
		name = getpropstring(node, "name");
		if (name == NULL)
			continue;
		if (strcmp("kb_ps2", name) == 0 ||
		    strcmp("keyboard", name) == 0) {
			if (node == OF_instance_to_package(OF_stdin()))
				return (1);
		}
	}
	return (0);
}


code:
/*	$OpenBSD: pcons.c,v 1.20 2013/10/20 20:07:27 miod Exp $	*/
/*	$NetBSD: pcons.c,v 1.7 2001/05/02 10:32:20 scw Exp $	*/

/*-
 * Copyright (c) 2000 Eduardo E. Horvath
 * 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.
 */

/*
 * Default console driver.  Uses the PROM or whatever
 * driver(s) are appropriate.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/time.h>
#include <sys/syslog.h>

#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <machine/conf.h>
#include <machine/cpu.h>
#include <machine/psl.h>

#include <dev/cons.h>

#include "wsdisplay.h"

#if NWSDISPLAY > 0
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#endif

struct pconssoftc {
	struct device of_dev;

#if NWSDISPLAY > 0
	int	sc_wsdisplay;
	u_int	sc_nscreens;
#endif

	struct tty *of_tty;
	struct timeout sc_poll_to;
	int of_flags;
};
/* flags: */
#define	OFPOLL		1

#define	OFBURSTLEN	128	/* max number of bytes to write in one chunk */

/* XXXXXXXX - this is in MI code in NetBSD */
/*
 * Stuff to handle debugger magic key sequences.
 */
#define CNS_LEN                 128
#define CNS_MAGIC_VAL(x)        ((x)&0x1ff)
#define CNS_MAGIC_NEXT(x)       (((x)>>9)&0x7f)
#define CNS_TERM                0x7f    /* End of sequence */

typedef struct cnm_state {
	int	cnm_state;
	u_short	*cnm_magic;
} cnm_state_t;
#ifdef DDB
#include <ddb/db_var.h>
#define cn_trap()	do { if (db_console) Debugger(); } while (0)
#else
#define cn_trap()
#endif
#define cn_isconsole(d)	((d) == cn_tab->cn_dev)
void cn_init_magic(cnm_state_t *cnm);
void cn_destroy_magic(cnm_state_t *cnm);
int cn_set_magic(char *magic);
int cn_get_magic(char *magic, int len);
/* This should be called for each byte read */
#ifndef cn_check_magic
#define cn_check_magic(d, k, s)                                         \
        do {                                                            \
		if (cn_isconsole(d)) {                                  \
			int v = (s).cnm_magic[(s).cnm_state];           \
			if ((k) == CNS_MAGIC_VAL(v)) {                  \
				(s).cnm_state = CNS_MAGIC_NEXT(v);      \
				if ((s).cnm_state == CNS_TERM) {        \
					cn_trap();                      \
                                        (s).cnm_state = 0;              \
				}                                       \
			} else {                                        \
				(s).cnm_state = 0;                      \
			}                                               \
		}                                                       \
	} while (/* CONSTCOND */ 0)
#endif

/* Encode out-of-band events this way when passing to cn_check_magic() */
#define CNC_BREAK               0x100

/* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */

#include <sparc64/dev/cons.h>

int pconsmatch(struct device *, void *, void *);
void pconsattach(struct device *, struct device *, void *);

struct cfattach pcons_ca = {
	sizeof(struct pconssoftc), pconsmatch, pconsattach
};

struct cfdriver pcons_cd = {
	NULL, "pcons", DV_TTY
};

extern struct cfdriver pcons_cd;
static struct cnm_state pcons_cnm_state;

static int pconsprobe(void);
static void pcons_wsdisplay_init(struct pconssoftc *);
extern struct consdev *cn_tab;

cons_decl(prom_);

int
pconsmatch(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct mainbus_attach_args *ma = aux;

	/* Only attach if no other console has attached. */
	return (strcmp("pcons", ma->ma_name) == 0 &&
	    cn_tab->cn_getc == prom_cngetc);
}

void pcons_poll(void *);

void
pconsattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct pconssoftc *sc = (struct pconssoftc *) self;
#if NWSDISPLAY > 0
	char buffer[128];
	extern struct consdev wsdisplay_cons;
	extern int wsdisplay_getc_dummy(dev_t);
#endif

	printf("\n");
	if (!pconsprobe())
		return;

#if NWSDISPLAY > 0
	/*
	 * Attach a dumb wsdisplay device if a wscons input driver has
	 * registered as the console, or is about to do so (usb keyboards).
	 */
	if (wsdisplay_cons.cn_getc != wsdisplay_getc_dummy)
		sc->sc_wsdisplay = 1;
	else {
		if (OF_getprop(OF_instance_to_package(stdin), "compatible",
		    buffer, sizeof(buffer)) != -1 &&
		   strncmp("usb", buffer, 3) == 0)
		sc->sc_wsdisplay = 1;
	}

	if (sc->sc_wsdisplay != 0) {
		pcons_wsdisplay_init(sc);
		return;
	}
#endif
	cn_init_magic(&pcons_cnm_state);
	cn_set_magic("+++++");
	timeout_set(&sc->sc_poll_to, pcons_poll, sc);
}

void pconsstart(struct tty *);
int pconsparam(struct tty *, struct termios *);

int
pconsopen(dev, flag, mode, p)
	dev_t dev;
	int flag, mode;
	struct proc *p;
{
	struct pconssoftc *sc;
	int unit = minor(dev);
	struct tty *tp;

	if (unit >= pcons_cd.cd_ndevs)
		return ENXIO;
	sc = pcons_cd.cd_devs[unit];
	if (!sc)
		return ENXIO;
#if NWSDISPLAY > 0
	if (sc->sc_wsdisplay != 0)
		return ENXIO;
#endif
	if (!(tp = sc->of_tty)) {
		sc->of_tty = tp = ttymalloc(0);
	}
	tp->t_oproc = pconsstart;
	tp->t_param = pconsparam;
	tp->t_dev = dev;
	cn_tab->cn_dev = dev;
	if (!(tp->t_state & TS_ISOPEN)) {
		ttychars(tp);
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_cflag = TTYDEF_CFLAG;
		tp->t_lflag = TTYDEF_LFLAG;
		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
		pconsparam(tp, &tp->t_termios);
		ttsetwater(tp);
	} else if ((tp->t_state & TS_XCLUDE) && suser(p, 0))
		return EBUSY;
	tp->t_state |= TS_CARR_ON;

	if (!(sc->of_flags & OFPOLL)) {
		sc->of_flags |= OFPOLL;
		timeout_add(&sc->sc_poll_to, 1);
	}

	return (*linesw[tp->t_line].l_open)(dev, tp, p);
}

int
pconsclose(dev, flag, mode, p)
	dev_t dev;
	int flag, mode;
	struct proc *p;
{
	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
	struct tty *tp = sc->of_tty;

	timeout_del(&sc->sc_poll_to);
	sc->of_flags &= ~OFPOLL;
	(*linesw[tp->t_line].l_close)(tp, flag, p);
	ttyclose(tp);
	return 0;
}

int
pconsread(dev, uio, flag)
	dev_t dev;
	struct uio *uio;
	int flag;
{
	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
	struct tty *tp = sc->of_tty;

	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
}

int
pconswrite(dev, uio, flag)
	dev_t dev;
	struct uio *uio;
	int flag;
{
	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
	struct tty *tp = sc->of_tty;

	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
}

int
pconsioctl(dev, cmd, data, flag, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flag;
	struct proc *p;
{
	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
	struct tty *tp = sc->of_tty;
	int error;

	if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
		return error;
	if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
		return error;
	return ENOTTY;
}

struct tty *
pconstty(dev)
	dev_t dev;
{
	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];

	return sc->of_tty;
}

int
pconsstop(tp, flag)
	struct tty *tp;
	int flag;
{
	return 0;
}

void
pconsstart(tp)
	struct tty *tp;
{
	struct clist *cl;
	int s, len;
	u_char buf[OFBURSTLEN];

	s = spltty();
	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
		splx(s);
		return;
	}
	tp->t_state |= TS_BUSY;
	splx(s);
	cl = &tp->t_outq;
	len = q_to_b(cl, buf, OFBURSTLEN);
	OF_write(stdout, buf, len);
	s = spltty();
	tp->t_state &= ~TS_BUSY;
	if (cl->c_cc) {
		tp->t_state |= TS_TIMEOUT;
		timeout_add(&tp->t_rstrt_to, 1);
	}
	if (cl->c_cc <= tp->t_lowat) {
		if (tp->t_state & TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup(cl);
		}
		selwakeup(&tp->t_wsel);
	}
	splx(s);
}

int
pconsparam(tp, t)
	struct tty *tp;
	struct termios *t;
{
	tp->t_ispeed = t->c_ispeed;
	tp->t_ospeed = t->c_ospeed;
	tp->t_cflag = t->c_cflag;
	return 0;
}

void
pcons_poll(aux)
	void *aux;
{
	struct pconssoftc *sc = aux;
	struct tty *tp = sc->of_tty;
	char ch;

	while (OF_read(stdin, &ch, 1) > 0) {
		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
		if (tp && (tp->t_state & TS_ISOPEN)) {
			if (ch == '\b')
				ch = '\177';
			(*linesw[tp->t_line].l_rint)(ch, tp);
		}
	}
	timeout_add(&sc->sc_poll_to, 1);
}

int
pconsprobe()
{
	if (!stdin) stdin = OF_stdin();
	if (!stdout) stdout = OF_stdout();

	return (stdin && stdout);
}

void
pcons_cnpollc(dev, on)
	dev_t dev;
	int on;
{
	struct pconssoftc *sc = NULL;

	if (pcons_cd.cd_devs)
		sc = pcons_cd.cd_devs[minor(dev)];

	if (sc == NULL)
		return;

	if (on) {
		if (sc->of_flags & OFPOLL)
			timeout_del(&sc->sc_poll_to);
		sc->of_flags &= ~OFPOLL;
	} else {
                /* Resuming kernel. */
		if (!(sc->of_flags & OFPOLL)) {
			sc->of_flags |= OFPOLL;
			timeout_add(&sc->sc_poll_to, 1);
		}
	}
}

/* XXXXXXXX --- more cnmagic stuff. */
#define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))

static unsigned short cn_magic[CNS_LEN];

/*
 * Initialize a cnm_state_t.
 */
void
cn_init_magic(cnm_state_t *cnm)
{
	cnm->cnm_state = 0;
	cnm->cnm_magic = cn_magic;
}

/*
 * Destroy a cnm_state_t.
 */
void
cn_destroy_magic(cnm_state_t *cnm)
{
	cnm->cnm_state = 0;
	cnm->cnm_magic = NULL;
}

/*
 * Translate a magic string to a state
 * machine table.
 */
int
cn_set_magic(char *magic)
{
	unsigned int i, c, n;
	unsigned short m[CNS_LEN];

	for (i=0; i<CNS_LEN; i++) {
		c = (*magic++)&0xff;
		n = *magic ? i+1 : CNS_TERM;
		switch (c) {
		case 0:
			/* End of string */
			if (i == 0) {
				/* empty string? */
				cn_magic[0] = 0;
#ifdef DEBUG
				printf("cn_set_magic(): empty!\n");
#endif
				return (0);
			}
			do {
				cn_magic[i] = m[i];
			} while (i--);
			return(0);
		case 0x27:
			/* Escape sequence */
			c = (*magic++)&0xff;
			n = *magic ? i+1 : CNS_TERM;
			switch (c) {
			case 0x27:
				break;
			case 0x01:
				/* BREAK */
				c = CNC_BREAK;
				break;
			case 0x02:
				/* NUL */
				c = 0;
				break;
			}
			/* FALLTHROUGH */
		default:
			/* Transition to the next state. */
#ifdef DEBUG
			if (!cold)
				printf("mag %d %x:%x\n", i, c, n);
#endif
			m[i] = ENCODE_STATE(c, n);
			break;
		}
	}
	return (EINVAL);
}

/*
 * Translate a state machine table back to
 * a magic string.
 */
int
cn_get_magic(char *magic, int maglen) {
	unsigned int i, c;

	for (i=0; i<CNS_LEN; i++) {
		c = cn_magic[i];
		/* Translate a character */
		switch (CNS_MAGIC_VAL(c)) {
		case CNC_BREAK:
			*magic++ = 0x27;
			*magic++ = 0x01;
			break;
		case 0:
			*magic++ = 0x27;
			*magic++ = 0x02;
			break;
		case 0x27:
			*magic++ = 0x27;
			*magic++ = 0x27;
			break;
		default:
			*magic++ = (c&0x0ff);
			break;
		}
		/* Now go to the next state */
		i = CNS_MAGIC_NEXT(c);
		if (i == CNS_TERM || i == 0) {
			/* Either termination state or empty machine */
			*magic++ = 0;
			return (0);
		}
	}
	return (EINVAL);
}

#if NWSDISPLAY > 0

int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
	    int *, int *, long *);
void	pcons_cursor(void *, int, int, int);
void	pcons_free_screen(void *, void *);
int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
int	pcons_mapchar(void *, int, unsigned int *);
paddr_t	pcons_mmap(void *, off_t, int);
int	pcons_putchar(void *, int, int, u_int, long);
int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
	    void *);

struct wsdisplay_emulops pcons_emulops = {
	NULL,
	pcons_mapchar,
	pcons_putchar
};

struct wsscreen_descr pcons_stdscreen = {
	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
};

const struct wsscreen_descr *pcons_scrlist[] = {
	&pcons_stdscreen
};

struct wsscreen_list pcons_screenlist = {
	1, pcons_scrlist
};

struct wsdisplay_accessops pcons_accessops = {
	.ioctl = pcons_ioctl,
	.mmap = pcons_mmap,
	.alloc_screen = pcons_alloc_screen,
	.free_screen = pcons_free_screen,
	.show_screen = pcons_show_screen
};

int
pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
    int *curxp, int *curyp, long *attrp)
{
	struct pconssoftc *sc = v;
	int *rowp, *colp;
	int row, col;

	if (sc->sc_nscreens > 0)
		return (ENOMEM);

	row = col = 0;
	if (romgetcursoraddr(&rowp, &colp) == 0) {
		if (rowp != NULL)
			row = *rowp;
		if (colp != NULL)
			col = *colp;
	}

	*cookiep = v;
	*attrp = 0;
	*curxp = col;
	*curyp = row;

	sc->sc_nscreens++;
	return (0);
}

void
pcons_free_screen(void *v, void *cookie)
{
	struct pconssoftc *sc = v;

	sc->sc_nscreens--;
}

int
pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
		break;
	default:
		return (-1);
	}

	return (0);
}

paddr_t
pcons_mmap(void *v, off_t off, int prot)
{
	return ((paddr_t)-1);
}

int
pcons_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *arg)
{
	return (0);
}

int
pcons_mapchar(void *v, int uc, unsigned int *idx)
{
	if ((uc & 0xff) == uc) {
		*idx = uc;
		return (1);
	} else {
		*idx = '?';
		return (0);
	}
}

int
pcons_putchar(void *v, int row, int col, u_int uc, long attr)
{
	u_char buf[1];
	int s;

	buf[0] = (u_char)uc;
	s = splhigh();
	OF_write(stdout, &buf, 1);
	splx(s);

	return 0;
}

void
pcons_wsdisplay_init(struct pconssoftc *sc)
{
	struct wsemuldisplaydev_attach_args waa;
	int *rowp, *colp;
	int options, row, col;

	row = col = 0;
	if (romgetcursoraddr(&rowp, &colp) == 0) {
		if (rowp != NULL)
			row = *rowp;
		if (colp != NULL)
			col = *colp;
	}

	options = OF_finddevice("/options");
	pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
	pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);

	/*
	 * We claim console here, because we can only get there if stdin
	 * is a keyboard. However, the PROM could have been configured with
	 * stdin being a keyboard and stdout being a serial sink.
	 * But since this combination is not supported under OpenBSD at the
	 * moment, it is reasonably safe to attach a dumb display as console
	 * here.
	 */
	wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);

	waa.console = 1;
	waa.scrdata = &pcons_screenlist;
	waa.accessops = &pcons_accessops;
	waa.accesscookie = sc;
	waa.defaultscreens = 1;

	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
}
#endif


code:
/*	$OpenBSD: pmc.c,v 1.4 2014/12/10 12:27:57 mikeb Exp $	*/

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

/*
 * Driver for watchdog device on Blade 1000, Fire 280R, Fire V480 etc.
 */

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

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

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

/*
 * Register access is indirect, through an address and data port.
 */

#define	PMC_ADDR	0
#define	PMC_DATA	1

/* Watchdog time-out register. */
#define PMC_WDTO	0x05

struct pmc_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

int	pmc_match(struct device *, void *, void *);
void	pmc_attach(struct device *, struct device *, void *);
int	pmc_activate(struct device *, int);
int	pmc_wdog_cb(void *, int);

struct cfattach pmc_ca = {
	sizeof(struct pmc_softc), pmc_match, pmc_attach, NULL, pmc_activate
};

struct cfdriver pmc_cd = {
	NULL, "pmc", DV_DULL
};

int
pmc_match(struct device *parent, void *cf, void *aux)
{
	struct ebus_attach_args *ea = aux;

	if (strcmp("pmc", ea->ea_name) == 0)
		return (1);
	return (0);
}

void
pmc_attach(struct device *parent, struct device *self, void *aux)
{
	struct pmc_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;

	/* Use prom address if available, otherwise map it. */
	if (ea->ea_nvaddrs) {
		if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
		    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh)) {
			printf(": can't map PROM register space\n");
			return;
		}
		sc->sc_iot = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf(": can't map register space\n");
		return;
	}

	printf("\n");

	wdog_register(pmc_wdog_cb, sc);
}

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

	return (0);
}

int
pmc_wdog_cb(void *arg, int period)
{
	struct pmc_softc *sc = arg;
	int mins;

	mins = (period + 59) / 60;
	if (mins > 255)
		mins = 255;

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, PMC_ADDR, PMC_WDTO);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, PMC_DATA, mins);

	return (mins * 60);
}


code:
/*	$OpenBSD: power.c,v 1.7 2014/07/11 08:18:31 guenther Exp $	*/

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

/*
 * Driver for power-button device on U5, U10, etc.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/timeout.h>
#include <sys/proc.h>
#include <sys/signalvar.h>

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

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

#define	POWER_REG		0

#define	POWER_REG_CPWR_OFF	0x00000002	/* courtesy power off */
#define	POWER_REG_SPWR_OFF	0x00000001	/* system power off */

struct power_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	struct intrhand		*sc_ih;
};

int	power_match(struct device *, void *, void *);
void	power_attach(struct device *, struct device *, void *);
int	power_intr(void *);

struct cfattach power_ca = {
	sizeof(struct power_softc), power_match, power_attach
};

struct cfdriver power_cd = {
	NULL, "power", DV_DULL
};

int
power_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct ebus_attach_args *ea = aux;

	if (strcmp(ea->ea_name, "power") == 0)
		return (1);
	return (0);
}

void
power_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct power_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;

	if (ea->ea_nregs < 1) {
		printf(": no registers\n");
		return;
	}

	/* Use prom address if available, otherwise map it. */
	if (ea->ea_nvaddrs) {
		if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
		    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh)) {
			printf(": can't map PROM register space\n");
			return;
		}
		sc->sc_iot = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf("%s: can't map register space\n", self->dv_xname);
		return;
	}

	if (ea->ea_nintrs > 0 && OF_getproplen(ea->ea_node, "button") >= 0) {
	        sc->sc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
		    IPL_BIO, 0, power_intr, sc, self->dv_xname);
		if (sc->sc_ih == NULL) {
			printf(": can't establish interrupt\n");
			return;
		}
	}
	printf("\n");
}

int
power_intr(void *vsc)
{
	extern int allowpowerdown;

	if (allowpowerdown == 1) {
		allowpowerdown = 0;
		prsignal(initprocess, SIGUSR2);
	}
	return (1);
}


code:
/*	$OpenBSD: ppm.c,v 1.1 2006/06/02 04:46:01 jason Exp $	*/

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

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

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

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

#define	BBC_GPIOBANK_INDEX	0x0
#define	BBC_GPIOBANK_DATA	0x1

#define	BBC_GPIO_PORT1_DATA	0x0
#define	BBC_GPIO_PORT2_DATA	0x4

#define	GPIO_P2D_LED		0x2

struct ppm_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_estarh;
	bus_space_handle_t	sc_rioh;
	bus_space_handle_t	sc_gpiobankh;
	bus_space_handle_t	sc_gpioh;
	struct blink_led	sc_blink;
};

int	ppm_match(struct device *, void *, void *);
void	ppm_attach(struct device *, struct device *, void *);

struct cfattach ppm_ca = {
	sizeof(struct ppm_softc), ppm_match, ppm_attach
};

struct cfdriver ppm_cd = {
	NULL, "ppm", DV_DULL
};

void ppm_led_blink(void *, int);

int
ppm_match(struct device *parent, void *match, void *aux)
{
	struct ebus_attach_args *ea = aux;

	if (strcmp(ea->ea_name, "ppm"))
		return (0);
	return (1);
}

void
ppm_attach(struct device *parent, struct device *self, void *aux)
{
	struct ppm_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;
	u_int8_t reg;

	sc->sc_iot = ea->ea_memtag;

	if (ea->ea_nregs < 4) {
		printf(": need %d regs\n", 4);
		return;
	}

#if 0
	if (ebus_bus_map(sc->sc_iot, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
	    0, 0, &sc->sc_estarh)) {
		printf(": failed to map estar\n");
		return;
	}
#endif
#if 0
	if (ebus_bus_map(sc->sc_iot, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[1]), ea->ea_regs[1].size,
	    0, 0, &sc->sc_rioh)) {
		printf(": failed to map riohr\n");
		return;
	}
#endif

	if (ebus_bus_map(sc->sc_iot, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[2]), ea->ea_regs[2].size,
	    0, 0, &sc->sc_gpiobankh)) {
		printf(": failed to map gpiobank\n");
		return;
	}

	if (ebus_bus_map(sc->sc_iot, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[3]), ea->ea_regs[3].size,
	    0, 0, &sc->sc_gpioh)) {
		printf(": failed to map gpio\n");
		return;
	}

	bus_space_write_1(sc->sc_iot, sc->sc_gpiobankh,
	    BBC_GPIOBANK_INDEX, 0x22);
	bus_space_read_1(sc->sc_iot, sc->sc_gpiobankh, BBC_GPIOBANK_INDEX);
	reg = bus_space_read_1(sc->sc_iot, sc->sc_gpiobankh, BBC_GPIOBANK_DATA);
	reg &= 0x7f;
	bus_space_write_1(sc->sc_iot, sc->sc_gpiobankh,BBC_GPIOBANK_INDEX, reg);
	bus_space_read_1(sc->sc_iot, sc->sc_gpiobankh, BBC_GPIOBANK_INDEX);

	sc->sc_blink.bl_func = ppm_led_blink;
	sc->sc_blink.bl_arg = sc;
	blink_led_register(&sc->sc_blink);
	printf("\n");
}

void
ppm_led_blink(void *vsc, int on)
{
	struct ppm_softc *sc = vsc;
	u_int8_t r;

	r = bus_space_read_1(sc->sc_iot, sc->sc_gpioh, BBC_GPIO_PORT1_DATA);
	if (on)
		r |= GPIO_P2D_LED;
	else
		r &= ~GPIO_P2D_LED;
	bus_space_write_1(sc->sc_iot, sc->sc_gpioh, BBC_GPIO_PORT1_DATA, r);
	bus_space_read_1(sc->sc_iot, sc->sc_gpioh, BBC_GPIO_PORT1_DATA);
}


code:
/*	$OpenBSD: prtc.c,v 1.3 2014/05/10 12:36:22 kettenis Exp $	*/

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

/*
 * Driver to get the time-of-day from the PROM for machines that don't
 * have a hardware real-time clock, like the Enterprise 10000,
 * Fire 12K and Fire 15K.
 */

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

#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <machine/sparc64.h>

#include <dev/clock_subr.h>

extern todr_chip_handle_t todr_handle;

int	prtc_match(struct device *, void *, void *);
void	prtc_attach(struct device *, struct device *, void *);

struct cfattach prtc_ca = {
	sizeof(struct device), prtc_match, prtc_attach
};

struct cfdriver prtc_cd = {
	NULL, "prtc", DV_DULL
};

int	prtc_gettime(todr_chip_handle_t, struct timeval *);
int	prtc_settime(todr_chip_handle_t, struct timeval *);

int
prtc_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *ma = aux;

	if (strcmp(ma->ma_name, "prtc") == 0)
		return (1);

	return (0);
}

void
prtc_attach(struct device *parent, struct device *self, void *aux)
{
	todr_chip_handle_t handle;

	printf("\n");

	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
	if (handle == NULL)
		panic("couldn't allocate todr_handle");

	handle->cookie = self;
	handle->todr_gettime = prtc_gettime;
	handle->todr_settime = prtc_settime;

	handle->bus_cookie = NULL;
	handle->todr_setwen = NULL;
	todr_handle = handle;
}

int
prtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
{
	u_int32_t tod = 0;
	char buf[32];

	if (OF_getprop(findroot(), "name", buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "SUNW,SPARC-Enterprise") == 0) {
		tv->tv_sec = prom_opl_get_tod();
		tv->tv_usec = 0;
		return (0);
	}

	snprintf(buf, sizeof(buf), "h# %08lx unix-gettod", (long)&tod);
	OF_interpret(buf, 0);

	tv->tv_sec = tod;
	tv->tv_usec = 0;
	return (0);
}

int
prtc_settime(todr_chip_handle_t handle, struct timeval *tv)
{
	return (0);
}


code:
/*	$OpenBSD: psycho.c,v 1.73 2014/07/12 22:37:03 uebayasi Exp $	*/
/*	$NetBSD: psycho.c,v 1.39 2001/10/07 20:30:41 eeh Exp $	*/

/*
 * Copyright (c) 1999, 2000 Matthew R. Green
 * Copyright (c) 2003 Henric Jungheim
 * 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.
 */

/*
 * Support for `psycho' and `psycho+' UPA to PCI bridge and
 * UltraSPARC IIi and IIe `sabre' PCI controllers.
 */

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

#include <uvm/uvm_extern.h>

#define _SPARC_BUS_DMA_PRIVATE
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <machine/psl.h>

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

#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/iommuvar.h>
#include <sparc64/dev/psychoreg.h>
#include <sparc64/dev/psychovar.h>
#include <sparc64/dev/starfire.h>
#include <sparc64/sparc64/cache.h>

#ifdef DEBUG
#define PDB_PROM	0x01
#define PDB_BUSMAP	0x02
#define PDB_INTR	0x04
#define PDB_CONF	0x08
int psycho_debug = ~0;
#define DPRINTF(l, s)   do { if (psycho_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

pci_chipset_tag_t psycho_alloc_chipset(struct psycho_pbm *, int,
    pci_chipset_tag_t);
void psycho_get_bus_range(int, int *);
void psycho_get_ranges(int, struct psycho_ranges **, int *);
void psycho_set_intr(struct psycho_softc *, int, void *,
    u_int64_t *, u_int64_t *, const char *);
bus_space_tag_t psycho_alloc_bus_tag(struct psycho_pbm *,
    const char *, int, int, int);

/* Interrupt handlers */
int psycho_ue(void *);
int psycho_ce(void *);
int psycho_bus_a(void *);
int psycho_bus_b(void *);
int psycho_bus_error(struct psycho_softc *, int);
int psycho_powerfail(void *);
int psycho_wakeup(void *);

/* IOMMU support */
void psycho_iommu_init(struct psycho_softc *, int);

/*
 * bus space and bus dma support for UltraSPARC `psycho'.  note that most
 * of the bus dma support is provided by the iommu dvma controller.
 */
int psycho_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    bus_size_t, int, bus_space_handle_t *);
paddr_t psycho_bus_mmap(bus_space_tag_t, bus_space_tag_t, bus_addr_t, off_t,
    int, int);
bus_addr_t psycho_bus_addr(bus_space_tag_t, bus_space_tag_t,
    bus_space_handle_t);
void *psycho_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
    int (*)(void *), void *, const char *);

int psycho_dmamap_create(bus_dma_tag_t, bus_dma_tag_t, bus_size_t, int,
    bus_size_t, bus_size_t, int, bus_dmamap_t *);
void psycho_sabre_dvmamap_sync(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
    bus_size_t, bus_size_t, int);
void psycho_map_psycho(struct psycho_softc *, int, bus_addr_t, bus_size_t,
    bus_addr_t, bus_size_t);
int psycho_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
void psycho_identify_pbm(struct psycho_softc *sc, struct psycho_pbm *pp,
    struct pcibus_attach_args *pa);

int psycho_conf_size(pci_chipset_tag_t, pcitag_t);
pcireg_t psycho_conf_read(pci_chipset_tag_t, pcitag_t, int);
void psycho_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t);

/* base pci_chipset */
extern struct sparc_pci_chipset _sparc_pci_chipset;

u_int stick_get_timecount(struct timecounter *);

struct timecounter stick_timecounter = {
	stick_get_timecount, NULL, ~0u, 0, "stick", 1000, NULL
};

/*
 * autoconfiguration
 */
int	psycho_match(struct device *, void *, void *);
void	psycho_attach(struct device *, struct device *, void *);
int	psycho_print(void *aux, const char *p);

struct cfattach psycho_ca = {
        sizeof(struct psycho_softc), psycho_match, psycho_attach
};

struct cfdriver psycho_cd = {
	NULL, "psycho", DV_DULL
};

/*
 * "sabre" is the UltraSPARC IIi onboard UPA to PCI bridge.  It manages a
 * single PCI bus and does not have a streaming buffer.  It often has an APB
 * (advanced PCI bridge) connected to it, which was designed specifically for
 * the IIi.  The APB let's the IIi handle two independednt PCI buses, and
 * appears as two "simba"'s underneath the sabre.
 *
 * "psycho" and "psycho+" is a dual UPA to PCI bridge.  It sits on the UPA bus
 * and manages two PCI buses.  "psycho" has two 64-bit 33MHz buses, while
 * "psycho+" controls both a 64-bit 33MHz and a 64-bit 66MHz PCI bus.  You
 * will usually find a "psycho+" since I don't think the original "psycho"
 * ever shipped, and if it did it would be in the U30.
 *
 * Each "psycho" PCI bus appears as a separate OFW node, but since they are
 * both part of the same IC, they only have a single register space.  As such,
 * they need to be configured together, even though the autoconfiguration will
 * attach them separately.
 *
 * On UltraIIi machines, "sabre" itself usually takes pci0, with "simba" often
 * as pci1 and pci2, although they have been implemented with other PCI bus
 * numbers on some machines.
 *
 * On UltraII machines, there can be any number of "psycho+" ICs, each
 * providing two PCI buses.
 *
 *
 * XXXX The psycho/sabre node has an `interrupts' attribute.  They contain
 * the values of the following interrupts in this order:
 *
 * PCI Bus Error	(30)
 * DMA UE		(2e)
 * DMA CE		(2f)
 * Power Fail		(25)
 *
 * We really should attach handlers for each.
 *
 */
#define	ROM_PCI_NAME		"pci"

struct psycho_type {
	char *p_name;
	int p_type;
} psycho_types[] = {
	{ "SUNW,psycho",        PSYCHO_MODE_PSYCHO      },
	{ "pci108e,8000",       PSYCHO_MODE_PSYCHO      },
	{ "SUNW,sabre",         PSYCHO_MODE_SABRE       },
	{ "pci108e,a000",       PSYCHO_MODE_SABRE       },
	{ "pci108e,a001",       PSYCHO_MODE_SABRE       },
	{ "pci10cf,138f",	PSYCHO_MODE_CMU_CH	},
	{ "pci10cf,1390",	PSYCHO_MODE_CMU_CH	},
	{ NULL, 0 }
};

int
psycho_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *ma = aux;
	struct psycho_type *ptype;
	char *str;

	/* match on a name of "pci" and a sabre or a psycho */
	if (strcmp(ma->ma_name, ROM_PCI_NAME) != 0)
		return (0);

	for (ptype = psycho_types; ptype->p_name != NULL; ptype++) {
		str = getpropstring(ma->ma_node, "model");
		if (strcmp(str, ptype->p_name) == 0)
			return (1);
		str = getpropstring(ma->ma_node, "compatible");
		if (strcmp(str, ptype->p_name) == 0)
			return (1);
	}
	return (0);
}

/*
 * SUNW,psycho initialization ...
 *	- find the per-psycho registers
 *	- figure out the IGN.
 *	- find our partner psycho
 *	- configure ourselves
 *	- bus range, bus,
 *	- get interrupt-map and interrupt-map-mask
 *	- setup the chipsets.
 *	- if we're the first of the pair, initialise the IOMMU, otherwise
 *	  just copy its tags and addresses.
 */
void
psycho_attach(struct device *parent, struct device *self, void *aux)
{
	struct psycho_softc *sc = (struct psycho_softc *)self;
	struct psycho_softc *osc = NULL;
	struct psycho_pbm *pp;
	struct pcibus_attach_args pba;
	struct mainbus_attach_args *ma = aux;
	u_int64_t csr;
	int psycho_br[2], n;
	struct psycho_type *ptype;
	char buf[32];
	u_int stick_rate;

	sc->sc_node = ma->ma_node;
	sc->sc_bustag = ma->ma_bustag;
	sc->sc_dmatag = ma->ma_dmatag;

	/*
	 * call the model-specific initialization routine.
	 */

	for (ptype = psycho_types; ptype->p_name != NULL; ptype++) {
		char *str;

		str = getpropstring(ma->ma_node, "model");
		if (strcmp(str, ptype->p_name) == 0)
			break;
		str = getpropstring(ma->ma_node, "compatible");
		if (strcmp(str, ptype->p_name) == 0)
			break;
	}
	if (ptype->p_name == NULL)
		panic("psycho_attach: unknown model?");
	sc->sc_mode = ptype->p_type;

	/*
	 * The psycho gets three register banks:
	 * (0) per-PBM configuration and status registers
	 * (1) per-PBM PCI configuration space, containing only the
	 *     PBM 256-byte PCI header
	 * (2) the shared psycho configuration registers (struct psychoreg)
	 *
	 * XXX use the prom address for the psycho registers?  we do so far.
	 */

	/* Register layouts are different.  stuupid. */
	if (sc->sc_mode == PSYCHO_MODE_PSYCHO ||
	    sc->sc_mode == PSYCHO_MODE_CMU_CH) {
		sc->sc_basepaddr = (paddr_t)ma->ma_reg[2].ur_paddr;

		if (ma->ma_naddress > 2) {
			psycho_map_psycho(sc, 0,
			    ma->ma_address[2], sizeof(struct psychoreg),
			    ma->ma_address[0], sizeof(struct pci_ctl));
		} else if (ma->ma_nreg > 2) {
			psycho_map_psycho(sc, 1,
			    ma->ma_reg[2].ur_paddr, ma->ma_reg[2].ur_len,
			    ma->ma_reg[0].ur_paddr, ma->ma_reg[0].ur_len);
		} else
			panic("psycho_attach: %d not enough registers",
			    ma->ma_nreg);
	} else {
		sc->sc_basepaddr = (paddr_t)ma->ma_reg[0].ur_paddr;

		if (ma->ma_naddress) {
			psycho_map_psycho(sc, 0,
			    ma->ma_address[0], sizeof(struct psychoreg),
			    ma->ma_address[0] +
				offsetof(struct psychoreg, psy_pcictl[0]),
			    sizeof(struct pci_ctl));
		} else if (ma->ma_nreg) {
			psycho_map_psycho(sc, 1,
			    ma->ma_reg[0].ur_paddr, ma->ma_reg[0].ur_len,
			    ma->ma_reg[0].ur_paddr +
				offsetof(struct psychoreg, psy_pcictl[0]),
			    sizeof(struct pci_ctl));
		} else
			panic("psycho_attach: %d not enough registers",
			    ma->ma_nreg);
	}

	csr = psycho_psychoreg_read(sc, psy_csr);
	sc->sc_ign = INTMAP_IGN; /* APB IGN is always 0x1f << 6 = 0x7c */
	if (sc->sc_mode == PSYCHO_MODE_PSYCHO ||
	    sc->sc_mode == PSYCHO_MODE_CMU_CH)
		sc->sc_ign = PSYCHO_GCSR_IGN(csr) << 6;

	printf(": %s, impl %d, version %d, ign %x\n", ptype->p_name,
	    PSYCHO_GCSR_IMPL(csr), PSYCHO_GCSR_VERS(csr), sc->sc_ign);

	/*
	 * Match other psycho's that are already configured against
	 * the base physical address. This will be the same for a
	 * pair of devices that share register space.
	 */
	for (n = 0; n < psycho_cd.cd_ndevs; n++) {
		struct psycho_softc *asc =
		    (struct psycho_softc *)psycho_cd.cd_devs[n];

		if (asc == NULL || asc == sc)
			/* This entry is not there or it is me */
			continue;

		if (asc->sc_basepaddr != sc->sc_basepaddr)
			/* This is an unrelated psycho */
			continue;

		/* Found partner */
		osc = asc;
		break;
	}

	/* Oh, dear.  OK, lets get started */

	/*
	 * Setup the PCI control register
	 */
	csr = psycho_pcictl_read(sc, pci_csr);
	csr |= PCICTL_MRLM | PCICTL_ARB_PARK | PCICTL_ERRINTEN |
	    PCICTL_4ENABLE;
	csr &= ~(PCICTL_SERR | PCICTL_CPU_PRIO | PCICTL_ARB_PRIO |
	    PCICTL_RTRYWAIT);
	psycho_pcictl_write(sc, pci_csr, csr);

	/*
	 * Allocate our psycho_pbm
	 */
	pp = sc->sc_psycho_this = malloc(sizeof *pp, M_DEVBUF,
		M_NOWAIT | M_ZERO);
	if (pp == NULL)
		panic("could not allocate psycho pbm");

	pp->pp_sc = sc;

	/* grab the psycho ranges */
	psycho_get_ranges(sc->sc_node, &pp->pp_range, &pp->pp_nrange);

	/* get the bus-range for the psycho */
	psycho_get_bus_range(sc->sc_node, psycho_br);

	bzero(&pba, sizeof(pba));
	pba.pba_domain = pci_ndomains++;
	pba.pba_bus = psycho_br[0];

	printf("%s: bus range %u-%u, PCI bus %d\n", sc->sc_dev.dv_xname,
	    psycho_br[0], psycho_br[1], psycho_br[0]);

	pp->pp_pcictl = sc->sc_pcictl;

	/* allocate our tags */
	pp->pp_memt = psycho_alloc_mem_tag(pp);
	pp->pp_iot = psycho_alloc_io_tag(pp);
	if (sc->sc_mode == PSYCHO_MODE_CMU_CH)
		pp->pp_dmat = ma->ma_dmatag;
	else
		pp->pp_dmat = psycho_alloc_dma_tag(pp);
	pp->pp_flags = (pp->pp_memt ? PCI_FLAGS_MEM_ENABLED : 0) |
	                (pp->pp_iot ? PCI_FLAGS_IO_ENABLED  : 0);

	/* allocate a chipset for this */
	pp->pp_pc = psycho_alloc_chipset(pp, sc->sc_node, &_sparc_pci_chipset);

	/* setup the rest of the psycho pbm */
	pba.pba_pc = pp->pp_pc;

	/*
	 * And finally, if we're a sabre or the first of a pair of psycho's to
	 * arrive here, start up the IOMMU and get a config space tag.
	 */

	if (osc == NULL) {
		uint64_t timeo;

		/* Initialize Starfire PC interrupt translation. */
		if (OF_getprop(findroot(), "name", buf, sizeof(buf)) > 0 &&
		    strcmp(buf, "SUNW,Ultra-Enterprise-10000") == 0)
			starfire_pc_ittrans_init(ma->ma_upaid);

		/*
		 * Establish handlers for interesting interrupts....
		 *
		 * XXX We need to remember these and remove this to support
		 * hotplug on the UPA/FHC bus.
		 *
		 * XXX Not all controllers have these, but installing them
		 * is better than trying to sort through this mess.
		 */
		psycho_set_intr(sc, 15, psycho_ue,
		    psycho_psychoreg_vaddr(sc, ue_int_map),
		    psycho_psychoreg_vaddr(sc, ue_clr_int), "ue");
		if (sc->sc_mode == PSYCHO_MODE_PSYCHO ||
		    sc->sc_mode == PSYCHO_MODE_SABRE) {
			psycho_set_intr(sc, 1, psycho_ce,
			    psycho_psychoreg_vaddr(sc, ce_int_map),
			    psycho_psychoreg_vaddr(sc, ce_clr_int), "ce");
			psycho_set_intr(sc, 15, psycho_bus_a,
			    psycho_psychoreg_vaddr(sc, pciaerr_int_map),
			    psycho_psychoreg_vaddr(sc, pciaerr_clr_int),
			    "bus_a");
		}
#if 0
		psycho_set_intr(sc, 15, psycho_powerfail,
		    psycho_psychoreg_vaddr(sc, power_int_map),
		    psycho_psychoreg_vaddr(sc, power_clr_int), "powerfail");
#endif
		if (sc->sc_mode == PSYCHO_MODE_PSYCHO ||
		    sc->sc_mode == PSYCHO_MODE_CMU_CH) {
			psycho_set_intr(sc, 15, psycho_bus_b,
			    psycho_psychoreg_vaddr(sc, pciberr_int_map),
			    psycho_psychoreg_vaddr(sc, pciberr_clr_int),
			    "bus_b");
		}
		if (sc->sc_mode == PSYCHO_MODE_PSYCHO) {
			psycho_set_intr(sc, 1, psycho_wakeup,
			    psycho_psychoreg_vaddr(sc, pwrmgt_int_map),
			    psycho_psychoreg_vaddr(sc, pwrmgt_clr_int),
			    "wakeup");
		}

		/*
		 * Apparently a number of machines with psycho and psycho+
		 * controllers have interrupt latency issues.  We'll try
		 * setting the interrupt retry timeout to 0xff which gives us
		 * a retry of 3-6 usec (which is what sysio is set to) for the
		 * moment, which seems to help alleviate this problem.
		 */
		timeo = psycho_psychoreg_read(sc, intr_retry_timer);
		if (timeo > 0xfff) {
#ifdef DEBUG
			printf("decreasing interrupt retry timeout "
			    "from %lx to 0xff\n", (long)timeo);
#endif
			psycho_psychoreg_write(sc, intr_retry_timer, 0xff);
		}

		/*
		 * Setup IOMMU and PCI configuration if we're the first
		 * of a pair of psycho's to arrive here.
		 *
		 * We should calculate a TSB size based on the amount of RAM,
		 * number of bus controllers, and number and type of child
		 * devices.
		 *
		 * For the moment, 32KB should be more than enough.
		 */
		sc->sc_is = malloc(sizeof(struct iommu_state),
			M_DEVBUF, M_NOWAIT | M_ZERO);
		if (sc->sc_is == NULL)
			panic("psycho_attach: malloc iommu_state");

		if (getproplen(sc->sc_node, "no-streaming-cache") < 0) {
			struct strbuf_ctl *sb = &pp->pp_sb;
			vaddr_t va = (vaddr_t)&pp->pp_flush[0x40];

			/*
			 * Initialize the strbuf_ctl.
			 *
			 * The flush sync buffer must be 64-byte aligned.
			 */

			sb->sb_flush = (void *)(va & ~0x3f);

			sb->sb_bustag = sc->sc_bustag;
			if (bus_space_subregion(sc->sc_bustag, sc->sc_pcictl,
			    offsetof(struct pci_ctl, pci_strbuf),
			    sizeof(struct iommu_strbuf),
			    &sb->sb_sb)) {
				printf("STC0 subregion failed\n");
				sb->sb_flush = 0;
			}
		}

		/* Point out iommu at the strbuf_ctl. */
		sc->sc_is->is_sb[0] = &pp->pp_sb;

		/* CMU-CH doesn't have an IOMMU. */
		if (sc->sc_mode != PSYCHO_MODE_CMU_CH) {
			printf("%s: ", sc->sc_dev.dv_xname);
			psycho_iommu_init(sc, 2);
		}

		sc->sc_configtag = psycho_alloc_config_tag(sc->sc_psycho_this);
		if (bus_space_map(sc->sc_configtag,
		    sc->sc_basepaddr, 0x01000000, 0, &sc->sc_configaddr))
			panic("can't map psycho PCI configuration space");
	} else {
		/* Just copy IOMMU state, config tag and address */
		sc->sc_is = osc->sc_is;
		sc->sc_configtag = osc->sc_configtag;
		sc->sc_configaddr = osc->sc_configaddr;

		if (getproplen(sc->sc_node, "no-streaming-cache") < 0) {
			struct strbuf_ctl *sb = &pp->pp_sb;
			vaddr_t va = (vaddr_t)&pp->pp_flush[0x40];

			/*
			 * Initialize the strbuf_ctl.
			 *
			 * The flush sync buffer must be 64-byte aligned.
			 */

			sb->sb_flush = (void *)(va & ~0x3f);

			sb->sb_bustag = sc->sc_bustag;
			if (bus_space_subregion(sc->sc_bustag, sc->sc_pcictl,
			    offsetof(struct pci_ctl, pci_strbuf),
			    sizeof(struct iommu_strbuf),
			    &sb->sb_sb)) {
				printf("STC1 subregion failed\n");
				sb->sb_flush = 0;
			}

			/* Point out iommu at the strbuf_ctl. */
			sc->sc_is->is_sb[1] = sb;
		}

		/* Point out iommu at the strbuf_ctl. */
		sc->sc_is->is_sb[1] = &pp->pp_sb;

		printf("%s: ", sc->sc_dev.dv_xname);
		printf("dvma map %x-%x", sc->sc_is->is_dvmabase,
		    sc->sc_is->is_dvmaend);
#ifdef DEBUG
		printf(", iotdb %llx-%llx",
		    (unsigned long long)sc->sc_is->is_ptsb,
		    (unsigned long long)(sc->sc_is->is_ptsb +
		    (PAGE_SIZE << sc->sc_is->is_tsbsize)));
#endif
		iommu_reset(sc->sc_is);
		printf("\n");
	}

	/*
	 * The UltraSPARC IIe has new STICK logic that provides a
	 * timebase counter that doesn't scale with processor
	 * frequency.  Use it to provide a timecounter.
	 */
	stick_rate = getpropint(findroot(), "stick-frequency", 0);
	if (stick_rate > 0 && sc->sc_mode == PSYCHO_MODE_SABRE) {
		stick_timecounter.tc_frequency = stick_rate;
		stick_timecounter.tc_priv = sc;
		tc_init(&stick_timecounter);
	}

	/*
	 * attach the pci.. note we pass PCI A tags, etc., for the sabre here.
	 */
	pba.pba_busname = "pci";
#if 0
	pba.pba_flags = sc->sc_psycho_this->pp_flags;
#endif
	pba.pba_dmat = sc->sc_psycho_this->pp_dmat;
	pba.pba_iot = sc->sc_psycho_this->pp_iot;
	pba.pba_memt = sc->sc_psycho_this->pp_memt;
	pba.pba_pc->bustag = sc->sc_configtag;
	pba.pba_pc->bushandle = sc->sc_configaddr;
	pba.pba_pc->conf_size = psycho_conf_size;
	pba.pba_pc->conf_read = psycho_conf_read;
	pba.pba_pc->conf_write = psycho_conf_write;
	pba.pba_pc->intr_map = psycho_intr_map;

	if (sc->sc_mode == PSYCHO_MODE_PSYCHO ||
	    sc->sc_mode == PSYCHO_MODE_CMU_CH)
		psycho_identify_pbm(sc, pp, &pba);
	else
		pp->pp_id = PSYCHO_PBM_UNKNOWN;

	config_found(self, &pba, psycho_print);
}

void
psycho_identify_pbm(struct psycho_softc *sc, struct psycho_pbm *pp,
    struct pcibus_attach_args *pa)
{
	vaddr_t pci_va = (vaddr_t)bus_space_vaddr(sc->sc_bustag, sc->sc_pcictl);
	paddr_t pci_pa;

	if (pmap_extract(pmap_kernel(), pci_va, &pci_pa) == 0)
	    pp->pp_id = PSYCHO_PBM_UNKNOWN;
	else switch(pci_pa & 0xffff) {
		case 0x2000:
			pp->pp_id = PSYCHO_PBM_A;
			break;
		case 0x4000:
			pp->pp_id = PSYCHO_PBM_B;
			break;
		default:
			pp->pp_id = PSYCHO_PBM_UNKNOWN;
			break;
	}
}

void
psycho_map_psycho(struct psycho_softc* sc, int do_map, bus_addr_t reg_addr,
    bus_size_t reg_size, bus_addr_t pci_addr, bus_size_t pci_size)
{
	if (do_map) {
		if (bus_space_map(sc->sc_bustag,
		    reg_addr, reg_size, 0, &sc->sc_regsh))
			panic("psycho_attach: cannot map regs");

		if (pci_addr >= reg_addr &&
		    pci_addr + pci_size <= reg_addr + reg_size) {
			if (bus_space_subregion(sc->sc_bustag, sc->sc_regsh,
			    pci_addr - reg_addr, pci_size, &sc->sc_pcictl))
				panic("psycho_map_psycho: map ctl");
		}
		else if (bus_space_map(sc->sc_bustag, pci_addr, pci_size,
		    0, &sc->sc_pcictl))
			panic("psycho_map_psycho: cannot map pci");
	} else {
		if (bus_space_map(sc->sc_bustag, reg_addr, reg_size,
		    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_regsh))
			panic("psycho_map_psycho: cannot map ctl");
		if (bus_space_map(sc->sc_bustag, pci_addr, pci_size,
		    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_pcictl))
			panic("psycho_map_psycho: cannot map pci");
	}
}

int
psycho_print(void *aux, const char *p)
{
	if (p == NULL)
		return (UNCONF);
	return (QUIET);
}

void
psycho_set_intr(struct psycho_softc *sc, int ipl, void *handler,
    u_int64_t *mapper, u_int64_t *clearer, const char *suffix)
{
	struct intrhand *ih;

	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (ih == NULL)
		panic("couldn't malloc intrhand");
	ih->ih_arg = sc;
	ih->ih_map = mapper;
	ih->ih_clr = clearer;
	ih->ih_fun = handler;
	ih->ih_pil = (1 << ipl);
	ih->ih_number = INTVEC(*(ih->ih_map));
	snprintf(ih->ih_name, sizeof(ih->ih_name),
	    "%s:%s", sc->sc_dev.dv_xname, suffix);

	DPRINTF(PDB_INTR, (
	    "\ninstalling handler %p arg %p for %s with number %x pil %u",
	    ih->ih_fun, ih->ih_arg, sc->sc_dev.dv_xname, ih->ih_number,
	    ih->ih_pil));

	intr_establish(ipl, ih);
}

/*
 * PCI bus support
 */

/*
 * allocate a PCI chipset tag and set its cookie.
 */
pci_chipset_tag_t
psycho_alloc_chipset(struct psycho_pbm *pp, int node, pci_chipset_tag_t pc)
{
	pci_chipset_tag_t npc;

	npc = malloc(sizeof *npc, M_DEVBUF, M_NOWAIT);
	if (npc == NULL)
		panic("could not allocate pci_chipset_tag_t");
	memcpy(npc, pc, sizeof *pc);
	npc->cookie = pp;
	npc->rootnode = node;

	return (npc);
}

/*
 * grovel the OBP for various psycho properties
 */
void
psycho_get_bus_range(node, brp)
	int node;
	int *brp;
{
	int n, error;

	error = getprop(node, "bus-range", sizeof(*brp), &n, (void **)&brp);
	if (error)
		panic("could not get psycho bus-range, error %d", error);
	if (n != 2)
		panic("broken psycho bus-range");
	DPRINTF(PDB_PROM,
	    ("psycho debug: got `bus-range' for node %08x: %u - %u\n",
	    node, brp[0], brp[1]));
}

void
psycho_get_ranges(int node, struct psycho_ranges **rp, int *np)
{

	if (getprop(node, "ranges", sizeof(**rp), np, (void **)rp))
		panic("could not get psycho ranges");
	DPRINTF(PDB_PROM,
	    ("psycho debug: got `ranges' for node %08x: %d entries\n",
	    node, *np));
}

/*
 * Interrupt handlers.
 */

int
psycho_ue(void *arg)
{
	struct psycho_softc *sc = arg;
	unsigned long long afsr = psycho_psychoreg_read(sc, psy_ue_afsr);
	unsigned long long afar = psycho_psychoreg_read(sc, psy_ue_afar);

	/*
	 * It's uncorrectable.  Dump the regs and panic.
	 */
	panic("%s: uncorrectable DMA error AFAR %llx (pa=%lx tte=%llx/%llx) "
	    "AFSR %llx", sc->sc_dev.dv_xname, afar,
	    iommu_extract(sc->sc_is, (vaddr_t)afar),
	    iommu_lookup_tte(sc->sc_is, (vaddr_t)afar),
	    iommu_fetch_tte(sc->sc_is, (paddr_t)afar),
	    afsr);
	return (1);
}

int
psycho_ce(void *arg)
{
	struct psycho_softc *sc = arg;
	u_int64_t afar, afsr;

	/*
	 * It's correctable.  Dump the regs and continue.
	 */

	afar = psycho_psychoreg_read(sc, psy_ce_afar);
	afsr = psycho_psychoreg_read(sc, psy_ce_afsr);

	printf("%s: correctable DMA error AFAR %llx AFSR %llx\n",
	    sc->sc_dev.dv_xname, afar, afsr);

	/* Clear error. */
	psycho_psychoreg_write(sc, psy_ce_afsr,
	    afsr & (PSY_CEAFSR_PDRD | PSY_CEAFSR_PDWR |
	    PSY_CEAFSR_SDRD | PSY_CEAFSR_SDWR));

	return (1);
}

int
psycho_bus_error(struct psycho_softc *sc, int bus)
{
	u_int64_t afsr, afar, bits;

	afar = psycho_psychoreg_read(sc, psy_pcictl[bus].pci_afar);
	afsr = psycho_psychoreg_read(sc, psy_pcictl[bus].pci_afsr);

	bits = afsr & (PSY_PCIAFSR_PMA | PSY_PCIAFSR_PTA | PSY_PCIAFSR_PTRY |
	    PSY_PCIAFSR_PPERR | PSY_PCIAFSR_SMA | PSY_PCIAFSR_STA |
	    PSY_PCIAFSR_STRY | PSY_PCIAFSR_SPERR);

	if (bits == 0)
		return (0);

	/*
	 * It's uncorrectable.  Dump the regs and panic.
	 */
	printf("%s: PCI bus %c error AFAR %llx (pa=%llx) AFSR %llx\n",
	    sc->sc_dev.dv_xname, 'A' + bus, (long long)afar,
	    (long long)iommu_extract(sc->sc_is, (vaddr_t)afar),
	    (long long)afsr);

	psycho_psychoreg_write(sc, psy_pcictl[bus].pci_afsr, bits);
	return (1);
}

int
psycho_bus_a(void *arg)
{
	struct psycho_softc *sc = arg;

	return (psycho_bus_error(sc, 0));
}

int
psycho_bus_b(void *arg)
{
	struct psycho_softc *sc = arg;

	return (psycho_bus_error(sc, 1));
}

int
psycho_powerfail(void *arg)
{
	/*
	 * We lost power.  Try to shut down NOW.
	 */
	panic("Power Failure Detected");
	/* NOTREACHED */
	return (1);
}

int
psycho_wakeup(void *arg)
{
	struct psycho_softc *sc = arg;

	/*
	 * Gee, we don't really have a framework to deal with this
	 * properly.
	 */
	printf("%s: power management wakeup\n",	sc->sc_dev.dv_xname);
	return (1);
}

/*
 * initialise the IOMMU..
 */
void
psycho_iommu_init(struct psycho_softc *sc, int tsbsize)
{
	struct iommu_state *is = sc->sc_is;
	int *vdma = NULL, nitem;
	u_int32_t iobase = -1;
	char *name;

	/* punch in our copies */
	is->is_bustag = sc->sc_bustag;
	bus_space_subregion(sc->sc_bustag, sc->sc_regsh,
	    offsetof(struct psychoreg, psy_iommu), sizeof(struct iommureg),
	    &is->is_iommu);

	/*
	 * Separate the men from the boys.  If it has a `virtual-dma'
	 * property, use it.
	 */
	if (!getprop(sc->sc_node, "virtual-dma", sizeof(vdma), &nitem,
	    (void **)&vdma)) {
		/* Damn.  Gotta use these values. */
		iobase = vdma[0];
#define	TSBCASE(x)	case 1 << ((x) + 23): tsbsize = (x); break
		switch (vdma[1]) {
			TSBCASE(1); TSBCASE(2); TSBCASE(3);
			TSBCASE(4); TSBCASE(5); TSBCASE(6);
		default:
			printf("bogus tsb size %x, using 7\n", vdma[1]);
			TSBCASE(7);
		}
#undef TSBCASE
		DPRINTF(PDB_CONF, ("psycho_iommu_init: iobase=0x%x\n", iobase));
		free(vdma, M_DEVBUF, 0);
	} else {
		DPRINTF(PDB_CONF, ("psycho_iommu_init: getprop failed, "
		    "iobase=0x%x, tsbsize=%d\n", iobase, tsbsize));
	}

	/* give us a nice name.. */
	name = (char *)malloc(32, M_DEVBUF, M_NOWAIT);
	if (name == NULL)
		panic("couldn't malloc iommu name");
	snprintf(name, 32, "%s dvma", sc->sc_dev.dv_xname);

	iommu_init(name, is, tsbsize, iobase);
}

/*
 * below here is bus space and bus dma support
 */

bus_space_tag_t
psycho_alloc_mem_tag(struct psycho_pbm *pp)
{
	return (psycho_alloc_bus_tag(pp, "mem",
	    0x02,	/* 32-bit mem space (where's the #define???) */
	    ASI_PRIMARY, ASI_PRIMARY_LITTLE));
}

bus_space_tag_t
psycho_alloc_io_tag(struct psycho_pbm *pp)
{
	return (psycho_alloc_bus_tag(pp, "io",
	    0x01,	/* IO space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
psycho_alloc_config_tag(struct psycho_pbm *pp)
{
	return (psycho_alloc_bus_tag(pp, "cfg",
	    0x00,	/* Config space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
psycho_alloc_bus_tag(struct psycho_pbm *pp,
    const char *name, int ss, int asi, int sasi)
{
	struct psycho_softc *sc = pp->pp_sc;
	struct sparc_bus_space_tag *bt;

	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (bt == NULL)
		panic("could not allocate psycho bus tag");

	snprintf(bt->name, sizeof(bt->name), "%s-pbm_%s(%d-%2.2x)",
	    sc->sc_dev.dv_xname, name, ss, asi);

	bt->cookie = pp;
	bt->parent = sc->sc_bustag;
	bt->default_type = ss;
	bt->asi = asi;
	bt->sasi = sasi;
	bt->sparc_bus_map = psycho_bus_map;
	bt->sparc_bus_mmap = psycho_bus_mmap;
	bt->sparc_bus_addr = psycho_bus_addr;
	bt->sparc_intr_establish = psycho_intr_establish;

	return (bt);
}

bus_dma_tag_t
psycho_alloc_dma_tag(struct psycho_pbm *pp)
{
	struct psycho_softc *sc = pp->pp_sc;
	bus_dma_tag_t dt, pdt = sc->sc_dmatag;

	dt = (bus_dma_tag_t)malloc(sizeof(struct sparc_bus_dma_tag),
	    M_DEVBUF, M_NOWAIT | M_ZERO);
	if (dt == NULL)
		panic("could not allocate psycho dma tag");

	dt->_cookie = pp;
	dt->_parent = pdt;
	dt->_dmamap_create	= psycho_dmamap_create;
	dt->_dmamap_destroy	= iommu_dvmamap_destroy;
	dt->_dmamap_load	= iommu_dvmamap_load;
	dt->_dmamap_load_raw	= iommu_dvmamap_load_raw;
	dt->_dmamap_unload	= iommu_dvmamap_unload;
	if (sc->sc_mode == PSYCHO_MODE_PSYCHO)
		dt->_dmamap_sync = iommu_dvmamap_sync;
	else
		dt->_dmamap_sync = psycho_sabre_dvmamap_sync;
	dt->_dmamem_alloc	= iommu_dvmamem_alloc;
	dt->_dmamem_free	= iommu_dvmamem_free;

	return (dt);
}

/*
 * bus space support.  <sparc64/dev/psychoreg.h> has a discussion about
 * PCI physical addresses.
 */

int
psycho_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t offset,
    bus_size_t size, int flags, bus_space_handle_t *hp)
{
	struct psycho_pbm *pp = t->cookie;
	int i, ss;

	DPRINTF(PDB_BUSMAP, ("\npsycho_bus_map: type %d off %qx sz %qx "
	    "flags %d", t->default_type, (unsigned long long)offset,
	    (unsigned long long)size, flags));

	ss = t->default_type;
	DPRINTF(PDB_BUSMAP, (" cspace %d", ss));

	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
		printf("\npsycho_bus_map: invalid parent");
		return (EINVAL);
	}

	t = t->parent;

	if (flags & BUS_SPACE_MAP_PROMADDRESS) {
		return ((*t->sparc_bus_map)
		    (t, t0, offset, size, flags, hp));
	}

	for (i = 0; i < pp->pp_nrange; i++) {
		bus_addr_t paddr;

		if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		paddr = pp->pp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi) << 32;
		DPRINTF(PDB_BUSMAP,
		    ("\n_psycho_bus_map: mapping paddr space %lx offset %lx "
			"paddr %qx",
		    (long)ss, (long)offset,
		    (unsigned long long)paddr));
		return ((*t->sparc_bus_map)(t, t0, paddr, size, flags, hp));
	}
	DPRINTF(PDB_BUSMAP, (" FAILED\n"));
	return (EINVAL);
}

paddr_t
psycho_bus_mmap(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t paddr,
    off_t off, int prot, int flags)
{
	bus_addr_t offset = paddr;
	struct psycho_pbm *pp = t->cookie;
	int i, ss;

	ss = t->default_type;

	DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_mmap: prot %d flags %d pa %qx",
	    prot, flags, (unsigned long long)paddr));

	if (t->parent == 0 || t->parent->sparc_bus_mmap == 0) {
		printf("\npsycho_bus_mmap: invalid parent");
		return (-1);
	}

	t = t->parent;

	for (i = 0; i < pp->pp_nrange; i++) {
		bus_addr_t paddr;

		if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		paddr = pp->pp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi) << 32;
		DPRINTF(PDB_BUSMAP, ("\npsycho_bus_mmap: mapping paddr "
		    "space %lx offset %lx paddr %qx",
		    (long)ss, (long)offset,
		    (unsigned long long)paddr));
		return ((*t->sparc_bus_mmap)(t, t0, paddr, off, prot, flags));
	}

	return (-1);
}

bus_addr_t
psycho_bus_addr(bus_space_tag_t t, bus_space_tag_t t0, bus_space_handle_t h)
{
	struct psycho_pbm *pp = t->cookie;
	bus_addr_t addr;
	int i, ss;

	ss = t->default_type;

	if (t->parent == 0 || t->parent->sparc_bus_addr == 0) {
		printf("\npsycho_bus_addr: invalid parent");
		return (-1);
	}

	t = t->parent;

	addr = ((*t->sparc_bus_addr)(t, t0, h));
	if (addr == -1)
		return (-1);

	for (i = 0; i < pp->pp_nrange; i++) {
		if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		return (BUS_ADDR_PADDR(addr) - pp->pp_range[i].phys_lo);
	}

	return (-1);
}

int
psycho_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
	return PCI_CONFIG_SPACE_SIZE;
}

pcireg_t
psycho_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
	struct cpu_info *ci = curcpu();
	pcireg_t val;
	int s;

	s = splhigh();
	membar(Sync);
	ci->ci_pci_probe = 1;
	val = bus_space_read_4(pc->bustag, pc->bushandle,
	    PCITAG_OFFSET(tag) + reg);
	membar(Sync);
	if (ci->ci_pci_fault)
		val = 0xffffffff;
	ci->ci_pci_probe = ci->ci_pci_fault = 0;
	splx(s);

	return (val);
}

void
psycho_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
        bus_space_write_4(pc->bustag, pc->bushandle,
	    PCITAG_OFFSET(tag) + reg, data);
}

/*
 * Bus-specific interrupt mapping
 */
int
psycho_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	struct psycho_pbm *pp = pa->pa_pc->cookie;
	struct psycho_softc *sc = pp->pp_sc;
	u_int dev;

	if (*ihp != (pci_intr_handle_t)-1) {
		*ihp |= sc->sc_ign;
		return (0);
	}

	/*
	 * We didn't find a PROM mapping for this interrupt.  Try to
	 * construct one ourselves based on the swizzled interrupt pin
	 * and the interrupt mapping for PCI slots documented in the
	 * UltraSPARC-IIi User's Manual.
	 */

	if (pa->pa_intrpin == 0)
		return (-1);

	/*
	 * This deserves some documentation.  Should anyone
	 * have anything official looking, please speak up.
	 */
	if (sc->sc_mode == PSYCHO_MODE_PSYCHO &&
	    pp->pp_id == PSYCHO_PBM_B)
		dev = PCITAG_DEV(pa->pa_intrtag) - 2;
	else
		dev = PCITAG_DEV(pa->pa_intrtag) - 1;

	*ihp = (pa->pa_intrpin - 1) & INTMAP_PCIINT;
	*ihp |= ((pp->pp_id == PSYCHO_PBM_B) ? INTMAP_PCIBUS : 0);
	*ihp |= (dev << 2) & INTMAP_PCISLOT;
	*ihp |= sc->sc_ign;

	return (0);
}

/*
 * install an interrupt handler for a PCI device
 */
void *
psycho_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
    int level, int flags, int (*handler)(void *), void *arg, const char *what)
{
	struct psycho_pbm *pp = t->cookie;
	struct psycho_softc *sc = pp->pp_sc;
	struct intrhand *ih;
	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
	int64_t intrmap = 0;
	int ino;
	long vec = INTVEC(ihandle);

	/*
	 * Hunt through all the interrupt mapping regs to look for our
	 * interrupt vector.
	 *
	 * XXX We only compare INOs rather than IGNs since the firmware may
	 * not provide the IGN and the IGN is constant for all device on that
	 * PCI controller.  This could cause problems for the FFB/external
	 * interrupt which has a full vector that can be set arbitrarily.
	 */

	DPRINTF(PDB_INTR,
	    ("\npsycho_intr_establish: ihandle %x vec %lx", ihandle, vec));
	ino = INTINO(vec);
	DPRINTF(PDB_INTR, (" ino %x", ino));

	/* If the device didn't ask for an IPL, use the one encoded. */
	if (level == IPL_NONE)
		level = INTLEV(vec);
	/* If it still has no level, print a warning and assign IPL 2 */
	if (level == IPL_NONE) {
		printf("ERROR: no IPL, setting IPL 2.\n");
		level = 2;
	}

	if (flags & BUS_INTR_ESTABLISH_SOFTINTR)
		goto found;

	DPRINTF(PDB_INTR,
	    ("\npsycho: intr %lx: %p\nHunting for IRQ...\n",
	    (long)ino, intrlev[ino]));

	/*
	 * First look for PCI interrupts, otherwise the PCI A slot 0
	 * INTA# interrupt might match an unused non-PCI (obio)
	 * interrupt.
	 */

	for (intrmapptr = psycho_psychoreg_vaddr(sc, pcia_slot0_int),
	    intrclrptr = psycho_psychoreg_vaddr(sc, pcia0_clr_int[0]);
	    intrmapptr <= (volatile u_int64_t *)
		psycho_psychoreg_vaddr(sc, pcib_slot3_int);
	    intrmapptr++, intrclrptr += 4) {
		/* Skip PCI-A Slot 2 and PCI-A Slot 3 on psycho's */
		if (sc->sc_mode == PSYCHO_MODE_PSYCHO &&
		    (intrmapptr ==
			psycho_psychoreg_vaddr(sc, pcia_slot2_int) ||
		    intrmapptr ==
			psycho_psychoreg_vaddr(sc, pcia_slot3_int)))
			continue;

		if (((*intrmapptr ^ vec) & 0x3c) == 0) {
			intrclrptr += vec & 0x3;
			goto found;
		}
	}

	/* Now hunt through obio.  */
	for (intrmapptr = psycho_psychoreg_vaddr(sc, scsi_int_map),
	    intrclrptr = psycho_psychoreg_vaddr(sc, scsi_clr_int);
	    intrmapptr < (volatile u_int64_t *)
		psycho_psychoreg_vaddr(sc, ffb0_int_map);
	    intrmapptr++, intrclrptr++) {
		if (INTINO(*intrmapptr) == ino)
			goto found;
	}

	printf("Cannot find interrupt vector %lx\n", vec);
	return (NULL);

found:
	ih = bus_intr_allocate(t0, handler, arg, ino | sc->sc_ign, level,
	    intrmapptr, intrclrptr, what);
	if (ih == NULL) {
		printf("Cannot allocate interrupt vector %lx\n", vec);
		return (NULL);
	}

	DPRINTF(PDB_INTR, (
	    "\ninstalling handler %p arg %p with number %x pil %u",
	    ih->ih_fun, ih->ih_arg, ih->ih_number, ih->ih_pil));

	if (flags & BUS_INTR_ESTABLISH_MPSAFE)
		ih->ih_mpsafe = 1;

	intr_establish(ih->ih_pil, ih);

	/*
	 * Enable the interrupt now we have the handler installed.
	 * Read the current value as we can't change it besides the
	 * valid bit so so make sure only this bit is changed.
	 *
	 * XXXX --- we really should use bus_space for this.
	 */
	if (intrmapptr) {
		intrmap = *intrmapptr;
		DPRINTF(PDB_INTR, ("; read intrmap = %016qx",
			(unsigned long long)intrmap));

		/* Enable the interrupt */
		intrmap |= INTMAP_V;
		DPRINTF(PDB_INTR, ("; addr of intrmapptr = %p", intrmapptr));
		DPRINTF(PDB_INTR, ("; writing intrmap = %016qx",
			(unsigned long long)intrmap));
		*intrmapptr = intrmap;
		DPRINTF(PDB_INTR, ("; reread intrmap = %016qx",
			(unsigned long long)(intrmap = *intrmapptr)));
	}
	return (ih);
}

/*
 * hooks into the iommu dvma calls.
 */
int
psycho_dmamap_create(bus_dma_tag_t t, bus_dma_tag_t t0, bus_size_t size,
    int nsegments, bus_size_t maxsegsz, bus_size_t boundary, int flags,
    bus_dmamap_t *dmamp)
{
	struct psycho_pbm *pp = t->_cookie;

	return (iommu_dvmamap_create(t, t0, &pp->pp_sb, size, nsegments,
	    maxsegsz, boundary, flags, dmamp));
}

void
psycho_sabre_dvmamap_sync(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
    bus_size_t offset, bus_size_t len, int ops)
{
	struct psycho_pbm *pp = t->_cookie;
	struct psycho_softc *sc = pp->pp_sc;

	if (ops & BUS_DMASYNC_POSTREAD)
		psycho_psychoreg_read(sc, pci_dma_write_sync);

	if (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_PREWRITE))
		membar(MemIssue);
}

u_int
stick_get_timecount(struct timecounter *tc)
{
	struct psycho_softc *sc = tc->tc_priv;

	return psycho_psychoreg_read(sc, stick_reg_low);
}