code:
/*	$OpenBSD: schizo.c,v 1.66 2014/07/12 18:44:43 tedu Exp $	*/

/*
 * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

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

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

#ifdef DDB
#include <machine/db_machdep.h>
#endif

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

#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/iommuvar.h>
#include <sparc64/dev/schizoreg.h>
#include <sparc64/dev/schizovar.h>
#include <sparc64/sparc64/cache.h>

#ifdef DEBUG
#define SDB_PROM        0x01
#define SDB_BUSMAP      0x02
#define SDB_INTR        0x04
#define SDB_CONF        0x08
int schizo_debug = ~0;
#define DPRINTF(l, s)   do { if (schizo_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

extern struct sparc_pci_chipset _sparc_pci_chipset;

int schizo_match(struct device *, void *, void *);
void schizo_attach(struct device *, struct device *, void *);
void schizo_init(struct schizo_softc *, int);
void schizo_init_iommu(struct schizo_softc *, struct schizo_pbm *);
int schizo_print(void *, const char *);

void schizo_set_intr(struct schizo_softc *, struct schizo_pbm *, int,
    int (*handler)(void *), void *, int, char *);
int schizo_ue(void *);
int schizo_ce(void *);
int schizo_safari_error(void *);
int schizo_pci_error(void *);

pci_chipset_tag_t schizo_alloc_chipset(struct schizo_pbm *, int,
    pci_chipset_tag_t);
bus_space_tag_t schizo_alloc_mem_tag(struct schizo_pbm *);
bus_space_tag_t schizo_alloc_io_tag(struct schizo_pbm *);
bus_space_tag_t schizo_alloc_config_tag(struct schizo_pbm *);
bus_space_tag_t schizo_alloc_bus_tag(struct schizo_pbm *, const char *,
    int, int, int);
bus_dma_tag_t schizo_alloc_dma_tag(struct schizo_pbm *);

int schizo_conf_size(pci_chipset_tag_t, pcitag_t);
pcireg_t schizo_conf_read(pci_chipset_tag_t, pcitag_t, int);
void schizo_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t);

int schizo_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
int schizo_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    bus_size_t, int, bus_space_handle_t *);
paddr_t schizo_bus_mmap(bus_space_tag_t, bus_space_tag_t, bus_addr_t, off_t,
    int, int);
bus_addr_t schizo_bus_addr(bus_space_tag_t, bus_space_tag_t,
    bus_space_handle_t);
void *schizo_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
    int (*)(void *), void *, const char *);

int schizo_dmamap_create(bus_dma_tag_t, bus_dma_tag_t, bus_size_t, int,
    bus_size_t, bus_size_t, int, bus_dmamap_t *);

#ifdef DDB
void schizo_xir(void *, int);
#endif

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

	if (strcmp(ma->ma_name, "pci") != 0)
		return (0);

	str = getpropstring(ma->ma_node, "model");
	if (strcmp(str, "schizo") == 0)
		return (1);

	str = getpropstring(ma->ma_node, "compatible");
	if (strcmp(str, "pci108e,8001") == 0)
		return (1);
	if (strcmp(str, "pci108e,8002") == 0)		/* XMITS */
		return (1);
	if (strcmp(str, "pci108e,a801") == 0)		/* Tomatillo */
		return (1);

	return (0);
}

void
schizo_attach(struct device *parent, struct device *self, void *aux)
{
	struct schizo_softc *sc = (struct schizo_softc *)self;
	struct mainbus_attach_args *ma = aux;
	int busa;
	char *str;

	str = getpropstring(ma->ma_node, "compatible");
	if (strcmp(str, "pci108e,a801") == 0)
		sc->sc_tomatillo = 1;

	sc->sc_node = ma->ma_node;
	sc->sc_dmat = ma->ma_dmatag;
	sc->sc_bust = ma->ma_bustag;
	sc->sc_ctrl = ma->ma_reg[1].ur_paddr - 0x10000UL;
	sc->sc_ign = INTIGN(ma->ma_upaid << INTMAP_IGN_SHIFT);

	if ((ma->ma_reg[0].ur_paddr & 0x00700000) == 0x00600000)
		busa = 1;
	else
		busa = 0;

	if (bus_space_map(sc->sc_bust, sc->sc_ctrl,
	    sizeof(struct schizo_regs), 0, &sc->sc_ctrlh)) {
		printf(": failed to map registers\n");
		return;
	}

	/* enable schizo ecc error interrupts */
	schizo_write(sc, SCZ_ECCCTRL, schizo_read(sc, SCZ_ECCCTRL) |
	    SCZ_ECCCTRL_EE_INTEN | SCZ_ECCCTRL_UE_INTEN |
	    SCZ_ECCCTRL_CE_INTEN);

	schizo_init(sc, busa);
}

void
schizo_init(struct schizo_softc *sc, int busa)
{
	struct schizo_pbm *pbm;
	struct pcibus_attach_args pba;
	int *busranges = NULL, nranges;
	u_int64_t match, reg;

	pbm = malloc(sizeof(*pbm), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (pbm == NULL)
		panic("schizo: can't alloc schizo pbm");

	pbm->sp_sc = sc;
	pbm->sp_bus_a = busa;
	pbm->sp_regt = sc->sc_bust;

	if (getprop(sc->sc_node, "ranges", sizeof(struct schizo_range),
	    &pbm->sp_nrange, (void **)&pbm->sp_range))
		panic("schizo: can't get ranges");

	if (getprop(sc->sc_node, "bus-range", sizeof(int), &nranges,
	    (void **)&busranges))
		panic("schizo: can't get bus-range");

	printf(": \"%s\", version %d, ign %x, bus %c %d to %d\n",
	    sc->sc_tomatillo ? "Tomatillo" : "Schizo",
	    getpropint(sc->sc_node, "version#", 0), sc->sc_ign,
	    busa ? 'A' : 'B', busranges[0], busranges[1]);

	if (bus_space_subregion(pbm->sp_regt, sc->sc_ctrlh,
	    busa ? offsetof(struct schizo_regs, pbm_a) :
	    offsetof(struct schizo_regs, pbm_b),
	    sizeof(struct schizo_pbm_regs),
	    &pbm->sp_regh)) {
		panic("schizo: unable to create PBM handle");
	}

	printf("%s: ", sc->sc_dv.dv_xname);
	schizo_init_iommu(sc, pbm);

	match = schizo_read(sc, busa ? SCZ_PCIA_IO_MATCH : SCZ_PCIB_IO_MATCH);
	pbm->sp_confpaddr = match & ~0x8000000000000000UL;

	pbm->sp_memt = schizo_alloc_mem_tag(pbm);
	pbm->sp_iot = schizo_alloc_io_tag(pbm);
	pbm->sp_cfgt = schizo_alloc_config_tag(pbm);
	pbm->sp_dmat = schizo_alloc_dma_tag(pbm);

	if (bus_space_map(pbm->sp_cfgt, 0, 0x1000000, 0, &pbm->sp_cfgh))
		panic("schizo: can't map config space");

	pbm->sp_pc = schizo_alloc_chipset(pbm, sc->sc_node,
	    &_sparc_pci_chipset);

	pbm->sp_pc->bustag = pbm->sp_cfgt;
	pbm->sp_pc->bushandle = pbm->sp_cfgh;

	bzero(&pba, sizeof(pba));
	pba.pba_busname = "pci";
	pba.pba_domain = pci_ndomains++;
	pba.pba_bus = busranges[0];
	pba.pba_pc = pbm->sp_pc;
#if 0
	pba.pba_flags = pbm->sp_flags;
#endif
	pba.pba_dmat = pbm->sp_dmat;
	pba.pba_memt = pbm->sp_memt;
	pba.pba_iot = pbm->sp_iot;
	pba.pba_pc->conf_size = schizo_conf_size;
	pba.pba_pc->conf_read = schizo_conf_read;
	pba.pba_pc->conf_write = schizo_conf_write;
	pba.pba_pc->intr_map = schizo_intr_map;

	free(busranges, M_DEVBUF, 0);

	schizo_pbm_write(pbm, SCZ_PCI_INTR_RETRY, 5);

	/* clear out the bus errors */
	schizo_pbm_write(pbm, SCZ_PCI_CTRL, schizo_pbm_read(pbm, SCZ_PCI_CTRL));
	schizo_pbm_write(pbm, SCZ_PCI_AFSR, schizo_pbm_read(pbm, SCZ_PCI_AFSR));
	schizo_cfg_write(pbm, PCI_COMMAND_STATUS_REG,
	    schizo_cfg_read(pbm, PCI_COMMAND_STATUS_REG));

	reg = schizo_pbm_read(pbm, SCZ_PCI_CTRL);
	/* enable/disable error interrupts and arbiter */
	reg |= SCZ_PCICTRL_EEN | SCZ_PCICTRL_MMU_INT | SCZ_PCICTRL_ARB;
	reg &= ~SCZ_PCICTRL_SBH_INT;
	schizo_pbm_write(pbm, SCZ_PCI_CTRL, reg);

	reg = schizo_pbm_read(pbm, SCZ_PCI_DIAG);
	reg &= ~(SCZ_PCIDIAG_D_RTRYARB | SCZ_PCIDIAG_D_RETRY |
	    SCZ_PCIDIAG_D_INTSYNC);
	schizo_pbm_write(pbm, SCZ_PCI_DIAG, reg);

	if (busa)
		schizo_set_intr(sc, pbm, PIL_HIGH, schizo_pci_error,
		   pbm, SCZ_PCIERR_A_INO, "pci_a");
	else
		schizo_set_intr(sc, pbm, PIL_HIGH, schizo_pci_error,
		   pbm, SCZ_PCIERR_B_INO, "pci_b");

	/* double mapped */
	schizo_set_intr(sc, pbm, PIL_HIGH, schizo_ue, sc, SCZ_UE_INO,
	    "ue");
	schizo_set_intr(sc, pbm, PIL_HIGH, schizo_ce, sc, SCZ_CE_INO,
	    "ce");
	schizo_set_intr(sc, pbm, PIL_HIGH, schizo_safari_error, sc,
	    SCZ_SERR_INO, "safari");

#ifdef DDB
	/*
	 * Only a master Tomatillo (the one with JPID[0:2] = 6)
	 * can/should generate XIR.
	 */
	if (sc->sc_tomatillo &&
	    ((schizo_read(sc, SCZ_CONTROL_STATUS) >> 20) & 0x7) == 6)
		db_register_xir(schizo_xir, sc);
#endif

	config_found(&sc->sc_dv, &pba, schizo_print);
}

int
schizo_ue(void *vsc)
{
	struct schizo_softc *sc = vsc;

	panic("%s: uncorrectable error", sc->sc_dv.dv_xname);
	return (1);
}

int
schizo_ce(void *vsc)
{
	struct schizo_softc *sc = vsc;

	panic("%s: correctable error", sc->sc_dv.dv_xname);
	return (1);
}

int
schizo_pci_error(void *vpbm)
{
	struct schizo_pbm *sp = vpbm;
	struct schizo_softc *sc = sp->sp_sc;
	u_int64_t afsr, afar, ctrl;
	u_int32_t csr;

	afsr = schizo_pbm_read(sp, SCZ_PCI_AFSR);
	afar = schizo_pbm_read(sp, SCZ_PCI_AFAR);
	ctrl = schizo_pbm_read(sp, SCZ_PCI_CTRL);
	csr = schizo_cfg_read(sp, PCI_COMMAND_STATUS_REG);

	printf("%s: pci bus %c error\n", sc->sc_dv.dv_xname,
	    sp->sp_bus_a ? 'A' : 'B');

	printf("PCIAFSR=%llb\n", afsr, SCZ_PCIAFSR_BITS);
	printf("PCIAFAR=%llx\n", afar);
	printf("PCICTRL=%llb\n", ctrl, SCZ_PCICTRL_BITS);
	printf("PCICSR=%b\n", csr, PCI_COMMAND_STATUS_BITS);

	if (ctrl & SCZ_PCICTRL_MMU_ERR) {
		u_int64_t ctrl, tfar;

		ctrl = schizo_pbm_read(sp, SCZ_PCI_IOMMU_CTRL);
		printf("IOMMUCTRL=%llx\n", ctrl);

		if ((ctrl & TOM_IOMMU_ERR) == 0)
			goto clear_error;

		if (sc->sc_tomatillo) {
			tfar = schizo_pbm_read(sp, TOM_PCI_IOMMU_TFAR);
			printf("IOMMUTFAR=%llx\n", tfar);
		}

		/* These are non-fatal if target abort was signalled. */
		if ((ctrl & TOM_IOMMU_ERR_MASK) == TOM_IOMMU_INV_ERR ||
		    ctrl & TOM_IOMMU_ILLTSBTBW_ERR ||
		    ctrl & TOM_IOMMU_BADVA_ERR) {
			if (csr & PCI_STATUS_TARGET_TARGET_ABORT) {
				schizo_pbm_write(sp, SCZ_PCI_IOMMU_CTRL, ctrl);
				goto clear_error;
			}
		}
	}

	panic("%s: fatal", sc->sc_dv.dv_xname);

 clear_error:
	schizo_cfg_write(sp, PCI_COMMAND_STATUS_REG, csr);
	schizo_pbm_write(sp, SCZ_PCI_CTRL, ctrl);
	schizo_pbm_write(sp, SCZ_PCI_AFSR, afsr);
	return (1);
}

int
schizo_safari_error(void *vsc)
{
	struct schizo_softc *sc = vsc;

	printf("%s: safari error\n", sc->sc_dv.dv_xname);

	printf("ERRLOG=%llx\n", schizo_read(sc, SCZ_SAFARI_ERRLOG));
	printf("UE_AFSR=%llx\n", schizo_read(sc, SCZ_UE_AFSR));
	printf("UE_AFAR=%llx\n", schizo_read(sc, SCZ_UE_AFAR));
	printf("CE_AFSR=%llx\n", schizo_read(sc, SCZ_CE_AFSR));
	printf("CE_AFAR=%llx\n", schizo_read(sc, SCZ_CE_AFAR));

	panic("%s: fatal", sc->sc_dv.dv_xname);
	return (1);
}

void
schizo_init_iommu(struct schizo_softc *sc, struct schizo_pbm *pbm)
{
	struct iommu_state *is = &pbm->sp_is;
	int *vdma = NULL, nitem, tsbsize = 7;
	u_int32_t iobase = -1;
	vaddr_t va;
	char *name;

	va = (vaddr_t)pbm->sp_flush[0x40];

	is->is_bustag = pbm->sp_regt;

	if (bus_space_subregion(is->is_bustag, pbm->sp_regh,
	    offsetof(struct schizo_pbm_regs, iommu),
	    sizeof(struct iommureg), &is->is_iommu)) {
		panic("schizo: unable to create iommu handle");
	}

	is->is_sb[0] = &pbm->sp_sb;
	is->is_sb[0]->sb_bustag = is->is_bustag;
	is->is_sb[0]->sb_flush = (void *)(va & ~0x3f);

	if (bus_space_subregion(is->is_bustag, pbm->sp_regh,
	    offsetof(struct schizo_pbm_regs, strbuf),
	    sizeof(struct iommu_strbuf), &is->is_sb[0]->sb_sb)) {
		panic("schizo: unable to create streaming buffer handle");
		is->is_sb[0]->sb_flush = NULL;
	}

#if 1
	/* XXX disable the streaming buffers for now */
	bus_space_write_8(is->is_bustag, is->is_sb[0]->sb_sb,
	    STRBUFREG(strbuf_ctl),
	    bus_space_read_8(is->is_bustag, is->is_sb[0]->sb_sb,
		STRBUFREG(strbuf_ctl)) & ~STRBUF_EN);
	is->is_sb[0]->sb_flush = NULL;
#endif

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

	/*
	 * Separate the men from the boys.  If the `virtual-dma'
	 * property exists, 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(SDB_BUSMAP, ("schizo_iommu_init: iobase=0x%x\n", iobase));
		free(vdma, M_DEVBUF, 0);
	} else {
		DPRINTF(SDB_BUSMAP, ("schizo_iommu_init: getprop failed, "
		    "using iobase=0x%x, tsbsize=%d\n", iobase, tsbsize));
	}

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

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

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

pcireg_t
schizo_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
schizo_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
schizo_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	struct schizo_pbm *sp = pa->pa_pc->cookie;
	struct schizo_softc *sc = sp->sp_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.
	 */
	dev = pa->pa_device - 1;

	*ihp = (pa->pa_intrpin - 1) & INTMAP_PCIINT;
	*ihp |= (dev << 2) & INTMAP_PCISLOT;
	*ihp |= sc->sc_ign;

	return (0);
}

void
schizo_set_intr(struct schizo_softc *sc, struct schizo_pbm *pbm, int ipl,
    int (*handler)(void *), void *arg, int ino, char *what)
{
	struct intrhand *ih;
	volatile u_int64_t *map, *clr;
	struct schizo_pbm_regs *pbmreg;
	char *name;
	int nlen;

	pbmreg = bus_space_vaddr(pbm->sp_regt, pbm->sp_regh);
	map = &pbmreg->imap[ino];
	clr = &pbmreg->iclr[ino];
	ino |= sc->sc_ign;

	nlen = strlen(sc->sc_dv.dv_xname) + 1 + strlen(what) + 1;
	name = malloc(nlen, M_DEVBUF, M_WAITOK);
	snprintf(name, nlen, "%s:%s", sc->sc_dv.dv_xname, what);

	ih = bus_intr_allocate(pbm->sp_regt, handler, arg, ino, ipl,
	    map, clr, name);
	if (ih == NULL) {
		printf("set_intr failed...\n");
		free(name, M_DEVBUF, 0);
		return;
	}

	intr_establish(ih->ih_pil, ih);
}

bus_space_tag_t
schizo_alloc_mem_tag(struct schizo_pbm *sp)
{
	return (schizo_alloc_bus_tag(sp, "mem",
	    0x02,       /* 32-bit mem space (where's the #define???) */
	    ASI_PRIMARY, ASI_PRIMARY_LITTLE));
}

bus_space_tag_t
schizo_alloc_io_tag(struct schizo_pbm *sp)
{
	return (schizo_alloc_bus_tag(sp, "io",
	    0x01,       /* IO space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
schizo_alloc_config_tag(struct schizo_pbm *sp)
{
	return (schizo_alloc_bus_tag(sp, "cfg",
	    0x00,       /* Config space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
schizo_alloc_bus_tag(struct schizo_pbm *pbm, const char *name, int ss,
    int asi, int sasi)
{
	struct schizo_softc *sc = pbm->sp_sc;
	struct sparc_bus_space_tag *bt;

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

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

	bt->cookie = pbm;
	bt->parent = sc->sc_bust;
	bt->default_type = ss;
	bt->asi = asi;
	bt->sasi = sasi;
	bt->sparc_bus_map = schizo_bus_map;
	bt->sparc_bus_mmap = schizo_bus_mmap;
	bt->sparc_bus_addr = schizo_bus_addr;
	bt->sparc_intr_establish = schizo_intr_establish;
	return (bt);
}

bus_dma_tag_t
schizo_alloc_dma_tag(struct schizo_pbm *pbm)
{
	struct schizo_softc *sc = pbm->sp_sc;
	bus_dma_tag_t dt, pdt = sc->sc_dmat;

	dt = malloc(sizeof(*dt), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (dt == NULL)
		panic("schizo: could not alloc dma tag");

	dt->_cookie = pbm;
	dt->_parent = pdt;
	dt->_dmamap_create	= schizo_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;
	dt->_dmamap_sync	= iommu_dvmamap_sync;
	dt->_dmamem_alloc	= iommu_dvmamem_alloc;
	dt->_dmamem_free	= iommu_dvmamem_free;
	return (dt);
}

pci_chipset_tag_t
schizo_alloc_chipset(struct schizo_pbm *pbm, int node, pci_chipset_tag_t pc)
{
	pci_chipset_tag_t npc;

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

int
schizo_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 schizo_pbm *sp = t->_cookie;

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

int
schizo_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 schizo_pbm *pbm = t->cookie;
	int i, ss;

	DPRINTF(SDB_BUSMAP, ("schizo_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(SDB_BUSMAP, (" cspace %d", ss));

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

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

	for (i = 0; i < pbm->sp_nrange; i++) {
		bus_addr_t paddr;

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

		paddr = pbm->sp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pbm->sp_range[i].phys_hi) << 32;
		return ((*t->parent->sparc_bus_map)
		    (t, t0, paddr, size, flags, hp));
	}

	return (EINVAL);
}

paddr_t
schizo_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 schizo_pbm *pbm = t->cookie;
	int i, ss;

	ss = t->default_type;

	DPRINTF(SDB_BUSMAP, ("schizo_bus_mmap: prot %d flags %d pa %qx\n",
	    prot, flags, (unsigned long long)paddr));

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

	for (i = 0; i < pbm->sp_nrange; i++) {
		bus_addr_t paddr;

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

		paddr = pbm->sp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pbm->sp_range[i].phys_hi) << 32;
		return ((*t->parent->sparc_bus_mmap)
		    (t, t0, paddr, off, prot, flags));
	}

	return (-1);
}

bus_addr_t
schizo_bus_addr(bus_space_tag_t t, bus_space_tag_t t0, bus_space_handle_t h)
{
	struct schizo_pbm *pbm = t->cookie;
	bus_addr_t addr;
	int i, ss;

	ss = t->default_type;

	if (t->parent == 0 || t->parent->sparc_bus_addr == 0) {
		printf("\nschizo_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 < pbm->sp_nrange; i++) {
		if (((pbm->sp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		return (BUS_ADDR_PADDR(addr) - pbm->sp_range[i].phys_lo);
	}

	return (-1);
}

void *
schizo_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 schizo_pbm *pbm = t->cookie;
	struct intrhand *ih = NULL;
	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
	int ino;
	long vec = INTVEC(ihandle);

	vec = INTVEC(ihandle);
	ino = INTINO(vec);

	if (level == IPL_NONE)
		level = INTLEV(vec);
	if (level == IPL_NONE) {
		printf(": no IPL, setting IPL 2.\n");
		level = 2;
	}

	if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
		struct schizo_pbm_regs *pbmreg;

		pbmreg = bus_space_vaddr(pbm->sp_regt, pbm->sp_regh);
		intrmapptr = &pbmreg->imap[ino];
		intrclrptr = &pbmreg->iclr[ino];
		if (INTIGN(vec) == 0)
			ino |= (*intrmapptr) & INTMAP_IGN;
		else
			ino |= vec & INTMAP_IGN;
	}

	ih = bus_intr_allocate(t0, handler, arg, ino, level, intrmapptr,
	    intrclrptr, what);
	if (ih == NULL)
		return (NULL);

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

	intr_establish(ih->ih_pil, ih);

	if (intrmapptr != NULL) {
		u_int64_t intrmap;

		intrmap = *intrmapptr;
		intrmap |= INTMAP_V;
		*intrmapptr = intrmap;
		intrmap = *intrmapptr;
		ih->ih_number |= intrmap & INTMAP_INR;
	}

	return (ih);
}

#ifdef DDB
void
schizo_xir(void *arg, int cpu)
{
	struct schizo_softc *sc = arg;

	schizo_write(sc, TOM_RESET_GEN, TOM_RESET_GEN_XIR);
}
#endif

const struct cfattach schizo_ca = {
	sizeof(struct schizo_softc), schizo_match, schizo_attach
};

struct cfdriver schizo_cd = {
	NULL, "schizo", DV_DULL
};


code:
/*	$OpenBSD: schizoreg.h,v 1.21 2008/12/16 22:27:34 kettenis 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.
 */
struct schizo_pbm_regs {
	volatile u_int64_t	_unused1[64];		/* 0x0000 - 0x01ff */
	struct iommureg		iommu;			/* 0x0200 - 0x03ff */
	volatile u_int64_t	_unused2[384];
	volatile u_int64_t	imap[64];
	volatile u_int64_t	_unused3[64];
	volatile u_int64_t	iclr[64];
	volatile u_int64_t	_unused4[320];
	volatile u_int64_t	ctrl;
	volatile u_int64_t	__unused0;
	volatile u_int64_t	afsr;
	volatile u_int64_t	afar;
	volatile u_int64_t	_unused5[252];
	struct iommu_strbuf	strbuf;
	volatile u_int64_t	strbuf_ctxflush;
	volatile u_int64_t	_unused6[4012];
	volatile u_int64_t	iommu_tag;
	volatile u_int64_t	_unused7[15];
	volatile u_int64_t	iommu_data;
	volatile u_int64_t	_unused8[63];
	volatile u_int64_t	istat[2];
	volatile u_int64_t	_unused9[2814];
	volatile u_int64_t	strbuf_ctxmatch;
	volatile u_int64_t	_unused10[122879];
};

struct schizo_regs {
	volatile u_int64_t	_unused0[8];
	volatile u_int64_t	pcia_mem_match;
	volatile u_int64_t	pcia_mem_mask;
	volatile u_int64_t	pcia_io_match;
	volatile u_int64_t	pcia_io_mask;
	volatile u_int64_t	pcib_mem_match;
	volatile u_int64_t	pcib_mem_mask;
	volatile u_int64_t	pcib_io_match;
	volatile u_int64_t	pcib_io_mask;
	volatile u_int64_t	_unused1[8176];

	volatile u_int64_t	control_status;
	volatile u_int64_t	error_control;
	volatile u_int64_t	interrupt_control;
	volatile u_int64_t	safari_errlog;
	volatile u_int64_t	eccctrl;
	volatile u_int64_t	_unused3[1];
	volatile u_int64_t	ue_afsr;
	volatile u_int64_t	ue_afar;
	volatile u_int64_t	ce_afsr;
	volatile u_int64_t	ce_afar;

	volatile u_int64_t	_unused4[253942];
	struct schizo_pbm_regs pbm_a;
	struct schizo_pbm_regs pbm_b;
};

#define	SCZ_PCIA_MEM_MATCH		0x00040
#define	SCZ_PCIA_MEM_MASK		0x00048
#define	SCZ_PCIA_IO_MATCH		0x00050
#define	SCZ_PCIA_IO_MASK		0x00058
#define	SCZ_PCIB_MEM_MATCH		0x00060
#define	SCZ_PCIB_MEM_MASK		0x00068
#define	SCZ_PCIB_IO_MATCH		0x00070
#define	SCZ_PCIB_IO_MASK		0x00078
#define	SCZ_CONTROL_STATUS		0x10000
#define	SCZ_SAFARI_INTCTRL		0x10010
#define	SCZ_SAFARI_ERRLOG		0x10018
#define	SCZ_ECCCTRL			0x10020
#define	SCZ_UE_AFSR			0x10030
#define	SCZ_UE_AFAR			0x10038
#define	SCZ_CE_AFSR			0x10040
#define	SCZ_CE_AFAR			0x10048
#define	TOM_RESET_GEN			0x17010

/* These are relative to the PBM */
#define	SCZ_PCI_IOMMU_CTRL		0x00200
#define	SCZ_PCI_IOMMU_TSBBASE		0x00208
#define	SCZ_PCI_IOMMU_FLUSH		0x00210
#define	SCZ_PCI_IOMMU_CTXFLUSH		0x00218
#define	TOM_PCI_IOMMU_TFAR		0x00220
#define	SCZ_PCI_IMAP_BASE		0x01000
#define	SCZ_PCI_ICLR_BASE		0x01400
#define	SCZ_PCI_INTR_RETRY		0x01a00	/* interrupt retry */
#define	SCZ_PCI_DMA_FLUSH		0x01a08	/* pci consistent dma flush */
#define	SCZ_PCI_CTRL			0x02000
#define	SCZ_PCI_AFSR			0x02010
#define	SCZ_PCI_AFAR			0x02018
#define	SCZ_PCI_DIAG			0x02020
#define	SCZ_PCI_ESTAR			0x02028
#define	SCZ_PCI_STRBUF_CTRL		0x02800
#define	SCZ_PCI_STRBUF_FLUSH		0x02808
#define	SCZ_PCI_STRBUF_FSYNC		0x02810
#define	SCZ_PCI_STRBUF_CTXFLUSH		0x02818
#define	SCZ_PCI_IOMMU_TAG		0x0a580
#define	SCZ_PCI_IOMMU_DATA		0x0a600
#define	SCZ_PCI_STRBUF_CTXMATCH		0x10000

#define	SCZ_ECCCTRL_EE_INTEN		0x8000000000000000UL
#define	SCZ_ECCCTRL_UE_INTEN		0x4000000000000000UL
#define	SCZ_ECCCTRL_CE_INTEN		0x2000000000000000UL

#define	SCZ_UEAFSR_PPIO			0x8000000000000000UL
#define	SCZ_UEAFSR_PDRD			0x4000000000000000UL
#define	SCZ_UEAFSR_PDWR			0x2000000000000000UL
#define	SCZ_UEAFSR_SPIO			0x1000000000000000UL
#define	SCZ_UEAFSR_SDMA			0x0800000000000000UL
#define	SCZ_UEAFSR_ERRPNDG		0x0300000000000000UL
#define	SCZ_UEAFSR_BMSK			0x000003ff00000000UL
#define	SCZ_UEAFSR_QOFF			0x00000000c0000000UL
#define	SCZ_UEAFSR_AID			0x000000001f000000UL
#define	SCZ_UEAFSR_PARTIAL		0x0000000000800000UL
#define	SCZ_UEAFSR_OWNEDIN		0x0000000000400000UL
#define	SCZ_UEAFSR_MTAGSYND		0x00000000000f0000UL
#define	SCZ_UEAFSR_MTAG			0x000000000000e000UL
#define	SCZ_UEAFSR_ECCSYND		0x00000000000001ffUL

#define	SCZ_UEAFAR_PIO			0x0000080000000000UL	/* 0=pio, 1=memory */
#define	SCZ_UEAFAR_PIO_TYPE		0x0000078000000000UL	/* pio type: */
#define	SCZ_UEAFAR_PIO_UPA		0x0000078000000000UL	/*  upa */
#define	SZC_UEAFAR_PIO_SAFARI		0x0000060000000000UL	/*  safari/upa64s */
#define	SCZ_UEAFAR_PIO_NLAS		0x0000058000000000UL	/*  newlink alt space */
#define	SCZ_UEAFAR_PIO_NLS		0x0000050000000000UL	/*  newlink space */
#define	SCZ_UEAFAR_PIO_NLI		0x0000040000000000UL	/*  newlink interface */
#define	SCZ_UEAFAR_PIO_PCIAM		0x0000030000000000UL	/*  pcia: memory */
#define	SCZ_UEAFAR_PIO_PCIAI		0x0000020000000000UL	/*  pcia: interface */
#define	SZC_UEAFAR_PIO_PCIBC		0x0000018000000000UL	/*  pcia: config / i/o */
#define	SZC_UEAFAR_PIO_PCIBM		0x0000010000000000UL	/*  pcib: memory */
#define	SZC_UEAFAR_PIO_PCIBI		0x0000000000000000UL	/*  pcib: interface */
#define	SCZ_UEAFAR_PIO_PCIAC		0x0000038000000000UL	/*  pcib: config / i/o */
#define	SCZ_UEAFAR_MEMADDR		0x000007fffffffff0UL	/* memory address */

#define	SCZ_CEAFSR_PPIO			0x8000000000000000UL
#define	SCZ_CEAFSR_PDRD			0x4000000000000000UL
#define	SCZ_CEAFSR_PDWR			0x2000000000000000UL
#define	SCZ_CEAFSR_SPIO			0x1000000000000000UL
#define	SCZ_CEAFSR_SDMA			0x0800000000000000UL
#define	SCZ_CEAFSR_ERRPNDG		0x0300000000000000UL
#define	SCZ_CEAFSR_BMSK			0x000003ff00000000UL
#define	SCZ_CEAFSR_QOFF			0x00000000c0000000UL
#define	SCZ_CEAFSR_AID			0x000000001f000000UL
#define	SCZ_CEAFSR_PARTIAL		0x0000000000800000UL
#define	SCZ_CEAFSR_OWNEDIN		0x0000000000400000UL
#define	SCZ_CEAFSR_MTAGSYND		0x00000000000f0000UL
#define	SCZ_CEAFSR_MTAG			0x000000000000e000UL
#define	SCZ_CEAFSR_ECCSYND		0x00000000000001ffUL

#define	SCZ_CEAFAR_PIO			0x0000080000000000UL	/* 0=pio, 1=memory */
#define	SCZ_CEAFAR_PIO_TYPE		0x0000078000000000UL	/* pio type: */
#define	SCZ_CEAFAR_PIO_UPA		0x0000078000000000UL	/*  upa */
#define	SZC_CEAFAR_PIO_SAFARI		0x0000060000000000UL	/*  safari/upa64s */
#define	SCZ_CEAFAR_PIO_NLAS		0x0000058000000000UL	/*  newlink alt space */
#define	SCZ_CEAFAR_PIO_NLS		0x0000050000000000UL	/*  newlink space */
#define	SCZ_CEAFAR_PIO_NLI		0x0000040000000000UL	/*  newlink interface */
#define	SCZ_CEAFAR_PIO_PCIAM		0x0000030000000000UL	/*  pcia: memory */
#define	SCZ_CEAFAR_PIO_PCIAI		0x0000020000000000UL	/*  pcia: interface */
#define	SZC_CEAFAR_PIO_PCIBC		0x0000018000000000UL	/*  pcia: config / i/o */
#define	SZC_CEAFAR_PIO_PCIBM		0x0000010000000000UL	/*  pcib: memory */
#define	SZC_CEAFAR_PIO_PCIBI		0x0000000000000000UL	/*  pcib: interface */
#define	SCZ_CEAFAR_PIO_PCIAC		0x0000038000000000UL	/*  pcib: config / i/o */
#define	SCZ_CEAFAR_MEMADDR		0x000007fffffffff0UL	/* memory address */

#define	TOM_RESET_GEN_XIR		0x00000000000000002L

#define	SCZ_PCICTRL_BUS_UNUS		(1UL << 63UL)		/* bus unusable */
#define	TOM_PCICTRL_DTO_ERR		(1UL << 62UL)		/* pci discard timeout */
#define	TOM_PCICTRL_DTO_INT		(1UL << 61UL)		/* discard intr en */
#define	SCZ_PCICTRL_ESLCK		(1UL << 51UL)		/* error slot locked */
#define	SCZ_PCICTRL_ERRSLOT		(7UL << 48UL)		/* error slot */
#define	SCZ_PCICTRL_TTO_ERR		(1UL << 38UL)		/* pci trdy# timeout */
#define	SCZ_PCICTRL_RTRY_ERR		(1UL << 37UL)		/* pci rtry# timeout */
#define	SCZ_PCICTRL_MMU_ERR		(1UL << 36UL)		/* pci mmu error */
#define	SCZ_PCICTRL_SBH_ERR		(1UL << 35UL)		/* pci strm hole */
#define	SCZ_PCICTRL_SERR		(1UL << 34UL)		/* pci serr# sampled */
#define	SCZ_PCICTRL_PCISPD		(1UL << 33UL)		/* speed (0=clk/2,1=clk) */
#define	SCZ_PCICTRL_PTO			(3UL << 24UL)		/* pci timeout interval */
#define	SCZ_PCICTRL_MMU_INT		(1UL << 19UL)		/* mmu intr en */
#define	SCZ_PCICTRL_SBH_INT		(1UL << 18UL)		/* strm byte hole intr en */
#define	SCZ_PCICTRL_EEN			(1UL << 17UL)		/* error intr en */
#define	SCZ_PCICTRL_PARK		(1UL << 16UL)		/* bus parked */
#define	SCZ_PCICTRL_PCIRST		(1UL <<  8UL)		/* pci reset */
#define	SCZ_PCICTRL_ARB			(0x3fUL << 0UL)		/* dma arb enables */
#define SCZ_PCICTRL_BITS "\20\277UNUS\276DTO\275DTO_INT\263ESLCK\246TTO\245RTRY\244MMU\243SBH\242SERR\241SPD\223MMU_INT\222SBH_INT\221EEN\220PARK\210PCIRST"

#define	SCZ_PCIAFSR_PMA			0x8000000000000000UL
#define	SCZ_PCIAFSR_PTA			0x4000000000000000UL
#define	SCZ_PCIAFSR_PRTRY		0x2000000000000000UL
#define	SCZ_PCIAFSR_PPERR		0x1000000000000000UL
#define	SCZ_PCIAFSR_PTTO		0x0800000000000000UL
#define	SCZ_PCIAFSR_PUNUS		0x0400000000000000UL
#define	SCZ_PCIAFSR_SMA			0x0200000000000000UL
#define	SCZ_PCIAFSR_STA			0x0100000000000000UL
#define	SCZ_PCIAFSR_SRTRY		0x0080000000000000UL
#define	SCZ_PCIAFSR_SPERR		0x0040000000000000UL
#define	SCZ_PCIAFSR_STTO		0x0020000000000000UL
#define	SCZ_PCIAFSR_SUNUS		0x0010000000000000UL
#define	SCZ_PCIAFSR_BMSK		0x000003ff00000000UL
#define	SCZ_PCIAFSR_BLK			0x0000000080000000UL
#define	SCZ_PCIAFSR_CFG			0x0000000040000000UL
#define	SCZ_PCIAFSR_MEM			0x0000000020000000UL
#define	SCZ_PCIAFSR_IO			0x0000000010000000UL

#define SCZ_PCIAFSR_BITS "\20\277PMA\276PTA\275PRTRY\274PPERR\273PTTO\272PUNUS\271SMA\270STA\267SRTRY\266SPERR\265STTO\264SUNUS\237BLK\236CFG\235MEM\234IO"

#define	SCZ_PCIDIAG_D_BADECC		(1UL << 10UL)	/* disable bad ecc */
#define	SCZ_PCIDIAG_D_BYPASS		(1UL <<  9UL)	/* disable mmu bypass */
#define	SCZ_PCIDIAG_D_TTO		(1UL <<  8UL)	/* disable trdy# timeout */
#define	SCZ_PCIDIAG_D_RTRYARB		(1UL <<  7UL)	/* disable retry arb */
#define	SCZ_PCIDIAG_D_RETRY		(1UL <<  6UL)	/* disable retry lim */
#define	SCZ_PCIDIAG_D_INTSYNC		(1UL <<  5UL)	/* disable write sync */
#define	SCZ_PCIDIAG_I_DMADPAR		(1UL <<  3UL)	/* invert dma parity */
#define	SCZ_PCIDIAG_I_PIODPAR		(1UL <<  2UL)	/* invert pio data parity */
#define	SCZ_PCIDIAG_I_PIOAPAR		(1UL <<  1UL)	/* invert pio addr parity */

#define	TOM_IOMMU_ERR			(1UL << 24)
#define	TOM_IOMMU_ERR_MASK		(3UL << 25)
#define	TOM_IOMMU_PROT_ERR		(0UL << 25)
#define	TOM_IOMMU_INV_ERR		(1UL << 25)
#define	TOM_IOMMU_TO_ERR		(2UL << 25)
#define	TOM_IOMMU_ECC_ERR		(3UL << 25)
#define	TOM_IOMMU_ILLTSBTBW_ERR		(1UL << 27)
#define	TOM_IOMMU_BADVA_ERR		(1UL << 28)

#define	SCZ_PBM_A_REGS			(0x600000UL - 0x400000UL)
#define	SCZ_PBM_B_REGS			(0x700000UL - 0x400000UL)

#define	SCZ_UE_INO			0x30	/* uncorrectable error */
#define	SCZ_CE_INO			0x31	/* correctable ecc error */
#define	SCZ_PCIERR_A_INO		0x32	/* PCI A bus error */
#define	SCZ_PCIERR_B_INO		0x33	/* PCI B bus error */
#define	SCZ_SERR_INO			0x34	/* safari interface error */

struct schizo_range {
	u_int32_t	cspace;
	u_int32_t	child_hi;
	u_int32_t	child_lo;
	u_int32_t	phys_hi;
	u_int32_t	phys_lo;
	u_int32_t	size_hi;
	u_int32_t	size_lo;
};

license: unknown


code:
/*	$OpenBSD: schizovar.h,v 1.10 2007/01/14 16:19:49 kettenis 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.
 */

struct schizo_pbm {
	struct schizo_softc *sp_sc;

	struct schizo_range *sp_range;
	pci_chipset_tag_t sp_pc;
	int sp_nreg;
	int sp_nrange;
	int sp_nintmap;

	bus_space_tag_t		sp_memt;
	bus_space_tag_t		sp_iot;
	bus_space_tag_t		sp_regt;
	bus_space_handle_t	sp_regh;
	bus_space_tag_t		sp_cfgt;
	bus_space_handle_t	sp_cfgh;
	bus_dma_tag_t		sp_dmat;
	int			sp_bus;
	int			sp_flags;
	int			sp_bus_a;
	bus_addr_t		sp_confpaddr;
	struct iommu_state	sp_is;
	struct strbuf_ctl	sp_sb;
	char			sp_flush[0x80];
};

struct schizo_softc {
	struct device sc_dv;
	int sc_node;
	int sc_ign;
	bus_dma_tag_t sc_dmat;
	bus_space_tag_t sc_bust;
	bus_addr_t sc_ctrl;
	bus_space_handle_t sc_ctrlh;

	int sc_tomatillo;
};

#define	schizo_read(sc,r) \
    bus_space_read_8((sc)->sc_bust, (sc)->sc_ctrlh, (r))
#define	schizo_write(sc,r,v) \
    bus_space_write_8((sc)->sc_bust, (sc)->sc_ctrlh, (r), (v))
#define	schizo_pbm_read(pbm,r) \
    bus_space_read_8((pbm)->sp_regt, (pbm)->sp_regh, (r))
#define	schizo_pbm_write(pbm,r,v) \
    bus_space_write_8((pbm)->sp_regt, (pbm)->sp_regh, (r), (v))
#define	schizo_cfg_read(pbm,r) \
    bus_space_read_4((pbm)->sp_cfgt, (pbm)->sp_cfgh, (r))
#define	schizo_cfg_write(pbm,r,v) \
    bus_space_write_4((pbm)->sp_cfgt, (pbm)->sp_cfgh, (r), (v))


code:
/*	$OpenBSD: ssm.c,v 1.2 2014/07/12 18:44:43 tedu 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.
 */

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

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

int	ssm_match(struct device *, void *, void *);
void	ssm_attach(struct device *, struct device *, void *);

struct cfattach ssm_ca = {
	sizeof(struct device), ssm_match, ssm_attach
};

struct cfdriver ssm_cd = {
	NULL, "ssm", DV_DULL
};

int	ssm_print(void *, const char *);

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

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

	return (0);
}

void
ssm_attach(struct device *parent, struct device *self, void *aux)
{
	struct mainbus_attach_args *ma = aux;
	struct mainbus_attach_args nma;
	char buf[32];
	int node;

	printf("\n");

	for (node = OF_child(ma->ma_node); node; node = OF_peer(node)) {
		if (!checkstatus(node))
			continue;

		OF_getprop(node, "name", buf, sizeof(buf));
		if (strcmp(buf, "cpu") == 0)
			OF_getprop(node, "compatible", buf, sizeof(buf));

		bzero(&nma, sizeof(nma));
		nma.ma_bustag = ma->ma_bustag;
		nma.ma_dmatag = ma->ma_dmatag;
		nma.ma_node = node;
		nma.ma_name = buf;
		nma.ma_upaid = getpropint(node, "portid", -1);
		getprop(node, "reg", sizeof(*nma.ma_reg),
		    &nma.ma_nreg, (void **)&nma.ma_reg);
		config_found(self, &nma, ssm_print);
		free(nma.ma_reg, M_DEVBUF, 0);
	}
}

int
ssm_print(void *aux, const char *name)
{
	struct mainbus_attach_args *ma = aux;

	if (name)
		printf("\"%s\" at %s", ma->ma_name, name);
	return (UNCONF);
}


code:
/*	$OpenBSD: starfire.c,v 1.1 2008/03/16 22:18:53 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.
 */

#include <sys/param.h>

#include <sparc64/dev/starfire.h>

void
starfire_pc_ittrans_init(int upaid)
{
	paddr_t pa;

	pa = STARFIRE_UPAID2UPS(upaid) | STARFIRE_PSI_BASE;
	pa |= (STARFIRE_PSI_PCREG_OFF | STARFIRE_PC_INT_MAP);

	/*
	 * Since we direct all interrupts to the boot processor, we
	 * simply program the port controller ITTR hardware with a
	 * single mapping.
	 */
	pa += CPU_UPAID * 0x10;
	stwa(pa, ASI_PHYS_NON_CACHED, STARFIRE_UPAID2HWMID(cpu_myid()));
}


code:
/*	$OpenBSD: starfire.h,v 1.1 2008/03/16 22:18:53 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.
 */

/*
 * Starfire-specific definitions.
 */

#define STARFIRE_IO_BASE	0x10000000000ULL

#define STARFIRE_UPS_MID_SHIFT	33
#define STARFIRE_UPS_BRD_SHIFT	36
#define STARFIRE_UPS_BUS_SHIFT	6

#define STARFIRE_PSI_BASE	0x100f8000000ULL
#define STARFIRE_PSI_PCREG_OFF	0x4000000ULL

#define STARFIRE_PC_PORT_ID	0x0000d0UL
#define STARFIRE_PC_INT_MAP	0x000200UL

#define STARFIRE_UPAID2HWMID(upaid) \
    (((upaid & 0x3c) << 1) | ((upaid & 0x40) >> 4) | (upaid & 0x3))

#define STARFIRE_UPAID2UPS(upaid) \
    (((u_int64_t)STARFIRE_UPAID2HWMID(upaid) << \
	STARFIRE_UPS_MID_SHIFT) | STARFIRE_IO_BASE)

void	starfire_pc_ittrans_init(int);


code:
/*	$OpenBSD: stp_sbus.c,v 1.10 2008/06/26 05:42:13 ray Exp $	*/
/*	$NetBSD: stp4020.c,v 1.23 2002/06/01 23:51:03 lukem Exp $	*/

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

/*
 * STP4020: SBus/PCMCIA bridge supporting one Type-3 PCMCIA card, or up to
 * two Type-1 and Type-2 PCMCIA cards..
 */

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

#include <dev/pcmcia/pcmciareg.h>
#include <dev/pcmcia/pcmciavar.h>
#include <dev/pcmcia/pcmciachip.h>

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

#include <dev/sbus/sbusvar.h>
#include <dev/sbus/stp4020reg.h>
#include <dev/sbus/stp4020var.h>

struct stp4020_sbus_softc {
	struct stp4020_softc stp;
	struct sparc_bus_space_tag *sc_bustag_le;
};

int	stpmatch(struct device *, void *, void *);
void	stpattach(struct device *, struct device *, void *);

struct cfattach stp_sbus_ca = {
	sizeof(struct stp4020_sbus_softc), stpmatch, stpattach
};

int
stpmatch(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct sbus_attach_args *sa = aux;

	return (strcmp("SUNW,pcmcia", sa->sa_name) == 0);
}

/*
 * Attach all the sub-devices we can find
 */
void
stpattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct sbus_attach_args *sa = aux;
	struct stp4020_sbus_softc *ssc = (void *)self;
	struct stp4020_softc *sc = (void *)self;
	int node;
	int i;
	bus_space_tag_t bt;
	bus_space_handle_t bh;

	node = sa->sa_node;

	/* Allocate little-endian bus tag */
	ssc->sc_bustag_le = malloc(sizeof(*sa->sa_bustag), M_DEVBUF, M_NOWAIT);
	if (ssc->sc_bustag_le == NULL)
		panic("could not allocate stp bus tag");
	*ssc->sc_bustag_le = *sa->sa_bustag;
	ssc->sc_bustag_le->asi = ASI_PRIMARY_LITTLE;

	/* Transfer bus tags */
	sc->sc_bustag = sa->sa_bustag;

	/* Set up per-socket static initialization */
	sc->sc_socks[0].sc = sc->sc_socks[1].sc = sc;
	sc->sc_socks[0].tag = sc->sc_socks[1].tag = sa->sa_bustag;

	if (sa->sa_nreg < 8) {
		printf(": only %d register sets\n", sa->sa_nreg);
		return;
	}

	if (sa->sa_nintr != 2) {
		printf(": expect 2 interrupt SBus levels; got %d\n",
		    sa->sa_nintr);
		return;
	}

#define STP4020_BANK_PROM	0
#define STP4020_BANK_CTRL	4
	for (i = 0; i < 8; i++) {

		/*
		 * STP4020 Register address map:
		 *	bank  0:   Forth PROM
		 *	banks 1-3: socket 0, windows 0-2
		 *	bank  4:   control registers
		 *	banks 5-7: socket 1, windows 0-2
		 */

		if (i == STP4020_BANK_PROM)
			/* Skip the PROM */
			continue;

		if (i == STP4020_BANK_CTRL)
			bt = sc->sc_bustag;
		else
			bt = ssc->sc_bustag_le;

		if (sbus_bus_map(bt, sa->sa_reg[i].sbr_slot,
		    sa->sa_reg[i].sbr_offset, sa->sa_reg[i].sbr_size, 0, 0,
		    &bh) != 0) {
			printf(": attach: cannot map registers\n");
			return;
		}

		if (i == STP4020_BANK_CTRL) {
			/*
			 * Copy tag and handle to both socket structures
			 * for easy access in control/status IO functions.
			 */
			sc->sc_socks[0].regs = sc->sc_socks[1].regs = bh;
		} else if (i < STP4020_BANK_CTRL) {
			/* banks 1-3 */
			sc->sc_socks[0].windows[i-1].winaddr = bh;
			sc->sc_socks[0].wintag = bt;
		} else {
			/* banks 5-7 */
			sc->sc_socks[1].windows[i-5].winaddr = bh;
			sc->sc_socks[1].wintag = bt;
		}
	}

	/*
	 * We get to use two SBus interrupt levels.
	 * The higher level we use for status change interrupts;
	 * the lower level for PC card I/O.
	 */
	bus_intr_establish(sa->sa_bustag, sa->sa_intr[1].sbi_pri,
	    IPL_NONE, 0, stp4020_statintr, sc, self->dv_xname);

	bus_intr_establish(sa->sa_bustag, sa->sa_intr[0].sbi_pri,
	    IPL_NONE, 0, stp4020_iointr, sc, self->dv_xname);

	stpattach_common(sc, sa->sa_frequency);
}


code:
/*	$OpenBSD: tda.c,v 1.7 2010/08/27 18:58:57 robert Exp $ */

/*
 * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
 * Copyright (c) 2008 Mark Kettenis <kettenis@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/kernel.h>
#include <sys/device.h>
#include <sys/sensors.h>

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

#include <dev/i2c/i2cvar.h>

/* fan control registers */
#define TDA_SYSFAN_REG		0xf0
#define TDA_CPUFAN_REG		0xf2
#define TDA_PSFAN_REG		0xf4

#define TDA_FANSPEED_MIN        0x0c
#define TDA_FANSPEED_MAX        0x3f

#define TDA_PSFAN_ON            0x1f
#define TDA_PSFAN_OFF           0x00

/* Internal and External temperature sensor numbers */
#define SENSOR_TEMP_EXT		0
#define SENSOR_TEMP_INT		1

#define CPU_TEMP_MAX		(67 * 1000000 + 273150000)
#define CPU_TEMP_MIN		(57 * 1000000 + 273150000)
#define SYS_TEMP_MAX		(35 * 1000000 + 273150000)
#define SYS_TEMP_MIN		(25 * 1000000 + 273150000)

struct tda_softc {
	struct device		sc_dev;
	i2c_tag_t		sc_tag;
	i2c_addr_t		sc_addr;

	u_int16_t		sc_cfan_speed;	/* current CPU fan speed */
	u_int16_t		sc_sfan_speed;	/* current SYS fan speed */

	int			sc_nsensors;
};
#define DEVNAME(_s)		((_s)->sc_dev.dv_xname)

int	tda_match(struct device *, void *, void *);
void	tda_attach(struct device *, struct device *, void *);

void	tda_setspeed(struct tda_softc *);
void	tda_adjust(void *);

struct cfattach tda_ca = {
	sizeof(struct tda_softc), tda_match, tda_attach
};

struct cfdriver tda_cd = {
	NULL, "tda", DV_DULL
};

void *tda_cookie;

int
tda_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;
	char name[32];

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

	/* Only attach on the Sun Blade 1000/2000. */
	if (OF_getprop(findroot(), "name", name, sizeof(name)) <= 0)
		return (0);
	if (strcmp(name, "SUNW,Sun-Blade-1000") != 0)
		return (0);

	return (1);
}

void
tda_attach(struct device *parent, struct device *self, void *aux)
{
	struct tda_softc *sc = (struct tda_softc *)self;
	struct i2c_attach_args *ia = aux;
	struct ksensordev *ksens;
	int i;

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

	printf("\n");

	/*
	 * Set the fans to maximum speed and save the power levels;
	 * the controller is write-only.
	 */
	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
	tda_setspeed(sc);

	/* Get the number of sensor devices. */
	for (i = 0; ; i++) {
		if (sensordev_get(i, &ksens) == ENOENT)
			break;
	}
	sc->sc_nsensors = i;

	if (!sc->sc_nsensors) {
		printf("%s: no temperature sensors found\n", DEVNAME(sc));
		return;
	}

	if (sensor_task_register(sc, tda_adjust, 10) == NULL) {
		printf("%s: unable to register update task\n", DEVNAME(sc));
		return;
	}

	tda_cookie = sc;
}

void
tda_setspeed(struct tda_softc *sc)
{
	u_int8_t cmd[2];

	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
		sc->sc_sfan_speed = TDA_FANSPEED_MAX;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd[0] = TDA_CPUFAN_REG;
	cmd[1] = sc->sc_cfan_speed;
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
		printf("%s: cannot write cpu-fan register\n",
		    DEVNAME(sc));
		iic_release_bus(sc->sc_tag, 0);
		return;
        }

	cmd[0] = TDA_SYSFAN_REG;
	cmd[1] = sc->sc_sfan_speed;
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
		printf("%s: cannot write system-fan register\n",
		    DEVNAME(sc));
		iic_release_bus(sc->sc_tag, 0);
		return;
        }

	iic_release_bus(sc->sc_tag, 0);

#if 0
        printf("%s: changed fan speed to cpu=%d system=%d\n",
               DEVNAME(sc), sc->sc_cfan_speed, sc->sc_sfan_speed);
#endif

}

void
tda_adjust(void *v)
{
	struct tda_softc *sc = v;
	struct ksensor *ks;
	u_int64_t ctemp, stemp;
	int i, err;

	/* Default to running the fans at maximum speed. */
	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;

	ctemp = stemp = 0;
	for (i = 0; i < sc->sc_nsensors; i++) {
		err = sensor_find(i, SENSOR_TEMP, SENSOR_TEMP_EXT, &ks);
		if (err) {
			printf("%s: failed to get external sensor\n",
			    DEVNAME(sc));
			goto out;
		}
		ctemp = MAX(ctemp, ks->value);

		err = sensor_find(i, SENSOR_TEMP, SENSOR_TEMP_INT, &ks);
		if (err) {
			printf("%s: failed to get internal sensors\n",
			    DEVNAME(sc));
			goto out;
		}
		stemp = MAX(stemp, ks->value);
	}

	if (ctemp < CPU_TEMP_MIN)
		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
	else if (ctemp < CPU_TEMP_MAX)
		sc->sc_cfan_speed = TDA_FANSPEED_MIN +
			(ctemp - CPU_TEMP_MIN) *
			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
			(CPU_TEMP_MAX - CPU_TEMP_MIN);

	if (stemp < SYS_TEMP_MIN)
		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
	else if (stemp < SYS_TEMP_MAX)
		sc->sc_sfan_speed = TDA_FANSPEED_MIN +
			(stemp - SYS_TEMP_MIN) *
			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
			(SYS_TEMP_MAX - SYS_TEMP_MIN);

out:
	tda_setspeed(sc);
}

/* This code gets called when we are about to drop to ddb,
 * in order to operate the fans at full speed during the
 * timeouts are not working.
 */
void
tda_full_blast()
{
	struct tda_softc *sc = tda_cookie;
	u_int8_t cmd[2];

	if (sc == NULL)
		return;

	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;

	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);

	cmd[0] = TDA_CPUFAN_REG;
	cmd[1] = sc->sc_cfan_speed;
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
		printf("%s: cannot write cpu-fan register\n",
		    DEVNAME(sc));
		iic_release_bus(sc->sc_tag, I2C_F_POLL);
		return;
        }

	cmd[0] = TDA_SYSFAN_REG;
	cmd[1] = sc->sc_sfan_speed;
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
		printf("%s: cannot write system-fan register\n",
		    DEVNAME(sc));
		iic_release_bus(sc->sc_tag, I2C_F_POLL);
		return;
	}

	iic_release_bus(sc->sc_tag, I2C_F_POLL);
}


code:
/*	$OpenBSD: upa.c,v 1.9 2010/11/11 17:58:23 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.
 *
 * 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/kernel.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/timeout.h>
#include <sys/malloc.h>

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

struct upa_range {
	u_int64_t	ur_space;
	u_int64_t	ur_addr;
	u_int64_t	ur_len;
};

struct upa_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_bt;
	bus_space_handle_t	sc_reg[3];
	struct upa_range	*sc_range;
	int			sc_node;
	int			sc_nrange;
	bus_space_tag_t		sc_cbt;
};

int	upa_match(struct device *, void *, void *);
void	upa_attach(struct device *, struct device *, void *);

struct cfattach upa_ca = {
	sizeof(struct upa_softc), upa_match, upa_attach
};

struct cfdriver upa_cd = {
	NULL, "upa", DV_DULL
};

int upa_print(void *, const char *);
bus_space_tag_t upa_alloc_bus_tag(struct upa_softc *);
int upa_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    bus_size_t, int, bus_space_handle_t *);
paddr_t upa_bus_mmap(bus_space_tag_t, bus_space_tag_t,
    bus_addr_t, off_t, int, int);

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

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

	return (0);
}

void
upa_attach(struct device *parent, struct device *self, void *aux)
{
	struct upa_softc *sc = (void *)self;
	struct mainbus_attach_args *ma = aux;
	int i, node;

	sc->sc_bt = ma->ma_bustag;
	sc->sc_node = ma->ma_node;

	for (i = 0; i < 3; i++) {
		if (i >= ma->ma_nreg) {
			printf(": no register %d\n", i);
			return;
		}
		if (bus_space_map(sc->sc_bt, ma->ma_reg[i].ur_paddr,
		    ma->ma_reg[i].ur_len, 0, &sc->sc_reg[i])) {
			printf(": failed to map reg%d\n", i);
			return;
		}
	}

	if (getprop(sc->sc_node, "ranges", sizeof(struct upa_range),
	    &sc->sc_nrange, (void **)&sc->sc_range))
		panic("upa: can't get ranges");

	printf("\n");

	sc->sc_cbt = upa_alloc_bus_tag(sc);

	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
		char buf[32];
		struct mainbus_attach_args map;

		bzero(&map, sizeof(map));
		if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
			continue;
		if (getprop(node, "reg", sizeof(*map.ma_reg),
		    &map.ma_nreg, (void **)&map.ma_reg) != 0)
			continue;
		if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
			continue;
		map.ma_node = node;
		map.ma_name = buf;
		map.ma_bustag = sc->sc_cbt;
		map.ma_dmatag = ma->ma_dmatag;
		config_found(&sc->sc_dev, &map, upa_print);
	}
}

int
upa_print(void *args, const char *name)
{
	struct mainbus_attach_args *ma = args;

	if (name)
		printf("\"%s\" at %s", ma->ma_name, name);
	return (UNCONF);
}

bus_space_tag_t
upa_alloc_bus_tag(struct upa_softc *sc)
{
	struct sparc_bus_space_tag *bt;

	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (bt == NULL)
		panic("upa: couldn't alloc bus tag");

	strlcpy(bt->name, sc->sc_dev.dv_xname, sizeof(bt->name));
	bt->cookie = sc;
	bt->parent = sc->sc_bt;
	bt->asi = bt->parent->asi;
	bt->sasi = bt->parent->sasi;
	bt->sparc_bus_map = upa_bus_map;
	bt->sparc_bus_mmap = upa_bus_mmap;
	/* XXX bt->sparc_intr_establish = upa_intr_establish; */
	return (bt);
}

int
upa_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 upa_softc *sc = t->cookie;
	int i;

	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
		printf("\n__upa_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 < sc->sc_nrange; i++) {
		if (offset < sc->sc_range[i].ur_space)
			continue;
		if (offset >= (sc->sc_range[i].ur_space +
		    sc->sc_range[i].ur_space))
			continue;
		break;
	}
	if (i == sc->sc_nrange)
		return (EINVAL);

	offset -= sc->sc_range[i].ur_space;
	offset += sc->sc_range[i].ur_addr;

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

paddr_t
upa_bus_mmap(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t paddr,
    off_t off, int prot, int flags)
{
	struct upa_softc *sc = t->cookie;
	int i;

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

	t = t->parent;

        if (flags & BUS_SPACE_MAP_PROMADDRESS)
		return ((*t->sparc_bus_mmap)(t, t0, paddr, off, prot, flags));

	for (i = 0; i < sc->sc_nrange; i++) {
		if (paddr + off < sc->sc_range[i].ur_space)
			continue;
		if (paddr + off >= (sc->sc_range[i].ur_space +
		    sc->sc_range[i].ur_space))
			continue;
		break;
	}
	if (i == sc->sc_nrange)
		return (EINVAL);

	paddr -= sc->sc_range[i].ur_space;
	paddr += sc->sc_range[i].ur_addr;

	return ((*t->sparc_bus_mmap)(t, t0, paddr, off, prot, flags));
}


code:
/*	$OpenBSD: uperf.c,v 1.6 2011/04/07 15:30:16 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.
 *
 * 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/kernel.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/conf.h>

#include <machine/conf.h>

#include <dev/sun/uperfio.h>
#include <arch/sparc64/dev/uperfvar.h>

struct cfdriver uperf_cd = {
	NULL, "uperf", DV_DULL
};

int uperf_getcntsrc(struct uperf_softc *, struct uperf_io *);
int uperf_findbyval(struct uperf_softc *, int, u_int, int *);
int uperf_findbysrc(struct uperf_softc *, int, int, u_int32_t *);
int uperf_setcntsrc(struct uperf_softc *, struct uperf_io *);

int
uperfopen(dev, flags, mode, p)
	dev_t dev;
	int flags, mode;
	struct proc *p;
{
	if (minor(dev) >= uperf_cd.cd_ndevs)
		return (ENXIO);
	if (uperf_cd.cd_devs[minor(dev)] == NULL)
		return (ENXIO);
	return (0);
}

int
uperfclose(dev, flags, mode, p)
	dev_t dev;
	int flags, mode;
	struct proc *p;
{
	return (0);
}

int
uperfioctl(dev, cmd, data, flags, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flags;
	struct proc *p;
{
	struct uperf_softc *usc = uperf_cd.cd_devs[minor(dev)];
	struct uperf_io *io = (struct uperf_io *)data;
	int error = EINVAL;

	switch (cmd) {
	case UPIO_GCNTSRC:
		error = uperf_getcntsrc(usc, io);
		break;
	case UPIO_SCNTSRC:
		error = uperf_setcntsrc(usc, io);
		break;
	case UPIO_CLRCNT:
		error = usc->usc_clrcnt(usc->usc_cookie, io->cnt_flags);
		break;
	case UPIO_GETCNT:
		error = usc->usc_getcnt(usc->usc_cookie, io->cnt_flags,
		    &io->cnt_val0, &io->cnt_val1);
		break;
	}

	return (error);
}

int
uperf_getcntsrc(usc, io)
	struct uperf_softc *usc;
	struct uperf_io *io;
{
	u_int cnt0_src, cnt1_src;
	int error;

	error = usc->usc_getcntsrc(usc->usc_cookie, io->cnt_flags,
	    &cnt0_src, &cnt1_src);
	if (error)
		return (error);

	if (io->cnt_flags & UPERF_CNT0) {
		error = uperf_findbyval(usc, UPERF_CNT0,
		    cnt0_src, &io->cnt_src0);
		if (error)
			return (error);
	}

	if (io->cnt_flags & UPERF_CNT1) {
		error = uperf_findbyval(usc, UPERF_CNT1,
		    cnt1_src, &io->cnt_src1);
		if (error)
			return (error);
	}
	return (0);
}

int
uperf_findbyval(usc, cnt, uval, rval)
	struct uperf_softc *usc;
	int cnt;
	u_int uval;
	int *rval;
{
	struct uperf_src *srcs = usc->usc_srcs;

	if (srcs->us_src == 0)
		return (EINVAL);

	while (srcs->us_src != -1) {
		if (srcs->us_val == uval && srcs->us_flags & cnt) {
			*rval = srcs->us_src;
			return (0);
		}
		srcs++;
	}
	return (EINVAL);
}

int
uperf_setcntsrc(usc, io)
	struct uperf_softc *usc;
	struct uperf_io *io;
{
	u_int32_t cnt0_src, cnt1_src;
	int error;

	if (io->cnt_flags & UPERF_CNT0) {
		error = uperf_findbysrc(usc, UPERF_CNT0,
		    io->cnt_src0, &cnt0_src);
		if (error)
			return (error);
	}
	if (io->cnt_flags & UPERF_CNT1) {
		error = uperf_findbysrc(usc, UPERF_CNT1,
		    io->cnt_src1, &cnt1_src);
		if (error)
			return (error);
	}
	return ((usc->usc_setcntsrc)(usc->usc_cookie, io->cnt_flags,
	    cnt0_src, cnt1_src));
}

int
uperf_findbysrc(usc, cnt, src, rval)
	struct uperf_softc *usc;
	int cnt, src;
	u_int32_t *rval;
{
	struct uperf_src *srcs = usc->usc_srcs;

	if (srcs->us_src == 0)
		return (EINVAL);

	while (srcs->us_src != -1) {
		if (srcs->us_src == src && srcs->us_flags & cnt) {
			*rval = srcs->us_val;
			return (0);
		}
		srcs++;
	}
	return (EINVAL);
}


code:
/*	$OpenBSD: uperf_ebus.c,v 1.5 2008/12/15 22:33:06 kettenis 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.
 *
 * 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/kernel.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/timeout.h>

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

#ifdef DDB
#include <machine/db_machdep.h>
#endif

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>
#include <dev/sun/uperfio.h>
#include <dev/sbus/uperf_sbusreg.h>
#include <sparc64/dev/uperfvar.h>
#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/psychoreg.h>

struct uperf_ebus_softc {
	struct uperf_softc	sc_usc;
	bus_space_tag_t		sc_bus_t;
	bus_space_handle_t	sc_bus_h;
};

int	uperf_ebus_match(struct device *, void *, void *);
void	uperf_ebus_attach(struct device *, struct device *, void *);

struct cfattach uperf_ebus_ca = {
	sizeof(struct uperf_ebus_softc), uperf_ebus_match, uperf_ebus_attach
};

u_int32_t uperf_ebus_read_reg(struct uperf_ebus_softc *, bus_size_t);
void uperf_ebus_write_reg(struct uperf_ebus_softc *,
    bus_size_t, u_int32_t);

int uperf_ebus_getcnt(void *, int, u_int32_t *, u_int32_t *);
int uperf_ebus_clrcnt(void *, int);
int uperf_ebus_getcntsrc(void *, int, u_int *, u_int *);
int uperf_ebus_setcntsrc(void *, int, u_int, u_int);

#ifdef DDB
void uperf_ebus_xir(void *, int);
#endif

struct uperf_src uperf_ebus_srcs[] = {
	{ UPERFSRC_SDVRA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SDVRA },
	{ UPERFSRC_SDVWA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SDVWA },
	{ UPERFSRC_CDVRA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_CDVRA },
	{ UPERFSRC_CDVWA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_CDVWA },
	{ UPERFSRC_SBMA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SBMA },
	{ UPERFSRC_DVA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_DVA },
	{ UPERFSRC_DVWA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_DVWA },
	{ UPERFSRC_PIOA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_PIOA },
	{ UPERFSRC_SDVRB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SDVRB },
	{ UPERFSRC_SDVWB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SDVWB },
	{ UPERFSRC_CDVRB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_CDVRB },
	{ UPERFSRC_CDVWB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_CDVWB },
	{ UPERFSRC_SBMB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_SBMB },
	{ UPERFSRC_DVB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_DVB },
	{ UPERFSRC_DVWB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_DVWB },
	{ UPERFSRC_PIOB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_PIOB },
	{ UPERFSRC_TLBMISS, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_TLBMISS },
	{ UPERFSRC_NINTRS, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_NINTRS },
	{ UPERFSRC_INACK, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_INACK },
	{ UPERFSRC_PIOR, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_PIOR },
	{ UPERFSRC_PIOW, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_PIOW },
	{ UPERFSRC_MERGE, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_MERGE },
	{ UPERFSRC_TBLA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_TBLA },
	{ UPERFSRC_STCA, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_STCA },
	{ UPERFSRC_TBLB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_TBLB },
	{ UPERFSRC_STCB, UPERF_CNT0|UPERF_CNT1, PSY_PMCRSEL_STCB },
	{ -1, -1, 0 }
};
int
uperf_ebus_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct ebus_attach_args *ea = aux;

	return (strcmp(ea->ea_name, "sc") == 0);
}

void
uperf_ebus_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct uperf_ebus_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;
	char *model;
	u_int32_t id;

	sc->sc_bus_t = ea->ea_memtag;
	sc->sc_usc.usc_cookie = sc;
	sc->sc_usc.usc_getcntsrc = uperf_ebus_getcntsrc;
	sc->sc_usc.usc_setcntsrc = uperf_ebus_setcntsrc;
	sc->sc_usc.usc_clrcnt = uperf_ebus_clrcnt;
	sc->sc_usc.usc_getcnt = uperf_ebus_getcnt;
	sc->sc_usc.usc_srcs = uperf_ebus_srcs;

	/* Use prom address if available, otherwise map it. */
	if (ea->ea_nregs != 1) {
		printf(": expected 1 register, got %d\n", ea->ea_nregs);
		return;
	}

	if (ebus_bus_map(sc->sc_bus_t, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
	    0, 0, &sc->sc_bus_h) != 0) {
		printf(": can't map register space\n");
		return;
	}

	id = uperf_ebus_read_reg(sc, USC_ID);
	model = getpropstring(ea->ea_node, "model");
	if (model == NULL || strlen(model) == 0)
		model = "unknown";

	printf(": model %s (%x/%x) ports %d\n", model,
	    (id & USC_ID_IMPL_M) >> USC_ID_IMPL_S,
	    (id & USC_ID_VERS_M) >> USC_ID_VERS_S,
	    (id & USC_ID_UPANUM_M) >> USC_ID_UPANUM_S);

#ifdef DDB
	db_register_xir(uperf_ebus_xir, sc);
#endif
}

/*
 * Read an indirect register.
 */
u_int32_t
uperf_ebus_read_reg(sc, r)
	struct uperf_ebus_softc *sc;
	bus_size_t r;
{
	u_int32_t v;
	int s;

	s = splhigh();
	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_ADDR, r);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_ADDR, 1,
	    BUS_SPACE_BARRIER_WRITE);

	/* Can't use multi reads because we have to gaurantee order */

	v = bus_space_read_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 0);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 0, 1,
	    BUS_SPACE_BARRIER_READ);

	v <<= 8;
	v |= bus_space_read_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 1);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 1, 1,
	    BUS_SPACE_BARRIER_READ);

	v <<= 8;
	v |= bus_space_read_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 2);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 2, 1,
	    BUS_SPACE_BARRIER_READ);

	v <<= 8;
	v |= bus_space_read_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 3);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 3, 1,
	    BUS_SPACE_BARRIER_READ);

	splx(s);
	return (v);
}

/*
 * Write an indirect register.
 */
void
uperf_ebus_write_reg(sc, r, v)
	struct uperf_ebus_softc *sc;
	bus_size_t r;
	u_int32_t v;
{
	int s;

	s = splhigh();
	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_ADDR, r);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_ADDR, 1,
	    BUS_SPACE_BARRIER_WRITE);

	/* Can't use multi writes because we have to gaurantee order */

	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 0,
	    (v >> 24) & 0xff);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 0, 1,
	    BUS_SPACE_BARRIER_WRITE);

	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 1,
	    (v >> 16) & 0xff);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 1, 1,
	    BUS_SPACE_BARRIER_WRITE);

	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 2,
	    (v >> 8) & 0xff);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 2, 1,
	    BUS_SPACE_BARRIER_WRITE);

	bus_space_write_1(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 3,
	    (v >> 0) & 0xff);
	bus_space_barrier(sc->sc_bus_t, sc->sc_bus_h, USC_DATA + 3, 1,
	    BUS_SPACE_BARRIER_WRITE);
	splx(s);
}

int
uperf_ebus_clrcnt(vsc, flags)
	void *vsc;
	int flags;
{
	struct uperf_ebus_softc *sc = vsc;
	u_int32_t clr = 0, oldsrc;

	if (flags & UPERF_CNT0)
		clr |= USC_PCTRL_CLR0;
	if (flags & UPERF_CNT1)
		clr |= USC_PCTRL_CLR1;
	if (clr) {
		oldsrc = uperf_ebus_read_reg(sc, USC_PERFCTRL);
		uperf_ebus_write_reg(sc, USC_PERFCTRL, clr | oldsrc);
	}
	return (0);
}

int
uperf_ebus_setcntsrc(vsc, flags, src0, src1)
	void *vsc;
	int flags;
	u_int src0, src1;
{
	struct uperf_ebus_softc *sc = vsc;
	u_int32_t src;

	src = uperf_ebus_read_reg(sc, USC_PERFCTRL);
	if (flags & UPERF_CNT0) {
		src &= ~USC_PCTRL_SEL0;
		src |= ((src0 << 0) & USC_PCTRL_SEL0) | USC_PCTRL_CLR0;
	}
	if (flags & UPERF_CNT1) {
		src &= ~USC_PCTRL_SEL1;
		src |= ((src1 << 8) & USC_PCTRL_SEL1) | USC_PCTRL_CLR1;
	}
	uperf_ebus_write_reg(sc, USC_PERFCTRL, src);
	return (0);
}

int
uperf_ebus_getcntsrc(vsc, flags, srcp0, srcp1)
	void *vsc;
	int flags;
	u_int *srcp0, *srcp1;
{
	struct uperf_ebus_softc *sc = vsc;
	u_int32_t src;

	src = uperf_ebus_read_reg(sc, USC_PERFCTRL);
	if (flags & UPERF_CNT0)
		*srcp0 = (src & USC_PCTRL_SEL0) >> 0;
	if (flags & UPERF_CNT1)
		*srcp1 = (src & USC_PCTRL_SEL1) >> 8;
	return (0);
}

int
uperf_ebus_getcnt(vsc, flags, cntp0, cntp1)
	void *vsc;
	int flags;
	u_int32_t *cntp0, *cntp1;
{
	struct uperf_ebus_softc *sc = vsc;
	u_int32_t c0, c1;

	c0 = uperf_ebus_read_reg(sc, USC_PERF0);
	c1 = uperf_ebus_read_reg(sc, USC_PERFSHAD);
	if (flags & UPERF_CNT0)
		*cntp0 = c0;
	if (flags & UPERF_CNT1)
		*cntp1 = c1;
	return (0);
}

#ifdef DDB
void
uperf_ebus_xir(void *arg, int cpu)
{
	struct uperf_ebus_softc *sc = arg;

	uperf_ebus_write_reg(sc, USC_CTRL, USC_CTRL_XIR);
}
#endif


code:
/*	$OpenBSD: uperfvar.h,v 1.4 2003/06/02 20:02:49 jason 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.
 *
 * 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.
 *
 */

struct uperf_softc {
	struct device sc_dv;
	void *usc_cookie;		/* cookie to pass upwards */
	int (*usc_getcntsrc)(void *, int, u_int *, u_int *);
	int (*usc_setcntsrc)(void *, int, u_int, u_int);
	int (*usc_clrcnt)(void *, int);
	int (*usc_getcnt)(void *, int, u_int32_t *, u_int32_t *);
	struct uperf_src *usc_srcs;
};

/* Table should be terminated with us_src = -1 */
struct uperf_src {
	int	us_src;		/* source number (user) */
	int	us_flags;	/* counters this source is valid for */
	u_int32_t us_val;	/* value to put in register */
};


code:
/*	$OpenBSD: vbus.c,v 1.7 2014/07/12 18:44:43 tedu 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.
 */

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

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

#include <sparc64/dev/vbusvar.h>

#include <dev/clock_subr.h>
extern todr_chip_handle_t todr_handle;

struct vbus_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_bustag;
	bus_dma_tag_t		sc_dmatag;
};

int	vbus_cmp_cells(int *, int *, int *, int);
int	vbus_match(struct device *, void *, void *);
void	vbus_attach(struct device *, struct device *, void *);
int	vbus_print(void *, const char *);

struct cfattach vbus_ca = {
	sizeof(struct vbus_softc), vbus_match, vbus_attach
};

struct cfdriver vbus_cd = {
	NULL, "vbus", DV_DULL
};

void	*vbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
    int (*)(void *), void *, const char *);
void	vbus_intr_ack(struct intrhand *);
bus_space_tag_t vbus_alloc_bus_tag(struct vbus_softc *, bus_space_tag_t);

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

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

	return (0);
}

void
vbus_attach(struct device *parent, struct device *self, void *aux)
{
	struct vbus_softc *sc = (struct vbus_softc *)self;
	struct mainbus_attach_args *ma = aux;
	int node;

	sc->sc_bustag = vbus_alloc_bus_tag(sc, ma->ma_bustag);
	sc->sc_dmatag = ma->ma_dmatag;
	printf("\n");

	for (node = OF_child(ma->ma_node); node; node = OF_peer(node)) {
		struct vbus_attach_args va;
		char buf[32];

		bzero(&va, sizeof(va));
		va.va_node = node;
		if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
			continue;
		va.va_name = buf;
		va.va_bustag = sc->sc_bustag;
		va.va_dmatag = sc->sc_dmatag;
		getprop(node, "reg", sizeof(*va.va_reg),
		    &va.va_nreg, (void **)&va.va_reg);
		getprop(node, "interrupts", sizeof(*va.va_intr),
		    &va.va_nintr, (void **)&va.va_intr);
		config_found(self, &va, vbus_print);
	}

	if (todr_handle == NULL) {
		struct vbus_attach_args va;

		bzero(&va, sizeof(va));
		va.va_name = "rtc";
		config_found(self, &va, vbus_print);
	}
}

int
vbus_print(void *aux, const char *name)
{
	struct vbus_attach_args *va = aux;

	if (name)
		printf("\"%s\" at %s", va->va_name, name);
	return (UNCONF);
}

/*
 * Compare a sequence of cells with a mask, return 1 if they match and
 * 0 if they don't.
 */
int
vbus_cmp_cells(int *cell1, int *cell2, int *mask, int ncells)
{
	int i;

	for (i = 0; i < ncells; i++) {
		if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
			return (0);
	}
	return (1);
}

int
vbus_intr_map(int node, int ino, uint64_t *sysino)
{
	int *imap = NULL, nimap;
	int *reg = NULL, nreg;
	int *imap_mask;
	int parent;
	int address_cells, interrupt_cells;
	uint64_t devhandle;
	uint64_t devino;
	int len;
	int err;

	parent = OF_parent(node);

	address_cells = getpropint(parent, "#address-cells", 2);
	interrupt_cells = getpropint(parent, "#interrupt-cells", 1);
	KASSERT(interrupt_cells == 1);

	len = OF_getproplen(parent, "interrupt-map-mask");
	if (len < (address_cells + interrupt_cells) * sizeof(int))
		return (-1);
	imap_mask = malloc(len, M_DEVBUF, M_NOWAIT);
	if (imap_mask == NULL)
		return (-1);
	if (OF_getprop(parent, "interrupt-map-mask", imap_mask, len) != len)
		return (-1);

	getprop(parent, "interrupt-map", sizeof(int), &nimap, (void **)&imap);
	getprop(node, "reg", sizeof(*reg), &nreg, (void **)&reg);
	if (nreg < address_cells)
		return (-1);

	while (nimap >= address_cells + interrupt_cells + 2) {
		if (vbus_cmp_cells(imap, reg, imap_mask, address_cells) &&
		    vbus_cmp_cells(&imap[address_cells], &ino,
		    &imap_mask[address_cells], interrupt_cells)) {
			node = imap[address_cells + interrupt_cells];
			devino = imap[address_cells + interrupt_cells + 1];

			free(reg, M_DEVBUF, 0);
			reg = NULL;

			getprop(node, "reg", sizeof(*reg), &nreg, (void **)&reg);
			devhandle = reg[0] & 0x0fffffff;

			err = hv_intr_devino_to_sysino(devhandle, devino, sysino);
			if (err != H_EOK)
				return (-1);

			KASSERT(*sysino == INTVEC(*sysino));
			return (0);
		}
		imap += address_cells + interrupt_cells + 2;
		nimap -= address_cells + interrupt_cells + 2;
	}

	return (-1);
}

void *
vbus_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)
{
	uint64_t sysino = INTVEC(ihandle);
	struct intrhand *ih;
	int err;

	ih = bus_intr_allocate(t0, handler, arg, ihandle, level,
	    NULL, NULL, what);
	if (ih == NULL)
		return (NULL);

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

	intr_establish(ih->ih_pil, ih);
	ih->ih_ack = vbus_intr_ack;

	err = hv_intr_settarget(sysino, cpus->ci_upaid);
	if (err != H_EOK)
		return (NULL);

	/* Clear pending interrupts. */
	err = hv_intr_setstate(sysino, INTR_IDLE);
	if (err != H_EOK)
		return (NULL);

	err = hv_intr_setenabled(sysino, INTR_ENABLED);
	if (err != H_EOK)
		return (NULL);

	return (ih);
}

void
vbus_intr_ack(struct intrhand *ih)
{
	hv_intr_setstate(ih->ih_number, INTR_IDLE);
}

bus_space_tag_t
vbus_alloc_bus_tag(struct vbus_softc *sc, bus_space_tag_t parent)
{
	struct sparc_bus_space_tag *bt;

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

	strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name));
	bt->cookie = sc;
	bt->parent = parent;
	bt->asi = parent->asi;
	bt->sasi = parent->sasi;
	bt->sparc_bus_map = parent->sparc_bus_map;
	bt->sparc_intr_establish = vbus_intr_establish;

	return (bt);
}


code:
/*	$OpenBSD: vbusvar.h,v 1.3 2008/12/30 21:23:33 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.
 */

#ifndef _SPARC64_DEV_VBUSVAR_H_
#define _SPARC64_DEV_VBUSVAR_H_

struct vbus_attach_args {
	char		*va_name;
	int		va_node;

	bus_space_tag_t	va_bustag;
	bus_dma_tag_t	va_dmatag;

	u_int32_t	*va_reg;
	u_int32_t	*va_intr;

	int		va_nreg;
	int		va_nintr;
};

int vbus_intr_map(int, int, uint64_t *);

#endif


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

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

#include <machine/autoconf.h>
#include <machine/hypervisor.h>
#include <machine/mdesc.h>

#include <sparc64/dev/cbusvar.h>

#ifdef VCC_DEBUG
#define DPRINTF(x)	printf x
#else
#define DPRINTF(x)
#endif

struct vcc_softc {
	struct device	sc_dv;
	bus_space_tag_t	sc_bustag;
	bus_dma_tag_t	sc_dmatag;
};

int	vcc_match(struct device *, void *, void *);
void	vcc_attach(struct device *, struct device *, void *);

struct cfattach vcc_ca = {
	sizeof(struct vcc_softc), vcc_match, vcc_attach
};

struct cfdriver vcc_cd = {
	NULL, "vcc", DV_DULL
};

void	vcc_get_channel_endpoint(int, struct cbus_attach_args *);

int
vcc_match(struct device *parent, void *match, void *aux)
{
	struct cbus_attach_args *ca = aux;

	if (strcmp(ca->ca_name, "virtual-console-concentrator") == 0)
		return (1);

	return (0);
}

void
vcc_attach(struct device *parent, struct device *self, void *aux)
{
	struct cbus_attach_args *ca = aux;
	struct cbus_attach_args nca;
	struct md_header *hdr;
	struct md_element *elem;
	const char *name_blk;
	const char *str;
	int idx;
	int arc;

	printf("\n");

	hdr = (struct md_header *)mdesc;
	elem = (struct md_element *)(mdesc + sizeof(struct md_header));
	name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz;

	idx = ca->ca_idx;
	for (; elem[idx].tag != 'E'; idx++) {
		str = name_blk + elem[idx].name_offset;
		if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
			continue;

		arc = elem[idx].d.val;
		str = name_blk + elem[arc].name_offset;
		if (strcmp(str, "virtual-device-port") == 0) {
			str = mdesc_get_prop_str(arc, "vcc-domain-name");
			if (str) {
				bzero(&nca, sizeof(nca));
				nca.ca_name = str;
				nca.ca_node = ca->ca_node;
				nca.ca_bustag = ca->ca_bustag;
				nca.ca_dmatag = ca->ca_dmatag;
				vcc_get_channel_endpoint(arc, &nca);
				config_found(self, &nca, cbus_print);
			}
		}
	}
}

void
vcc_get_channel_endpoint(int idx, struct cbus_attach_args *ca)
{
	struct md_header *hdr;
	struct md_element *elem;
	const char *name_blk;
	const char *str;
	int arc;

	hdr = (struct md_header *)mdesc;
	elem = (struct md_element *)(mdesc + sizeof(struct md_header));
	name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz;

	ca->ca_idx = idx;

	ca->ca_id = -1;
	ca->ca_tx_ino = -1;
	ca->ca_rx_ino = -1;

	for (; elem[idx].tag != 'E'; idx++) {
		str = name_blk + elem[idx].name_offset;
		if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
			continue;

		arc = elem[idx].d.val;
		str = name_blk + elem[arc].name_offset;
		if (strcmp(str, "channel-endpoint") == 0) {
			ca->ca_id = mdesc_get_prop_val(arc, "id");
			ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino");
			ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino");
			return;
		}
	}
}