code:
/*	$OpenBSD: ebus.c,v 1.23 2014/07/12 18:44:43 tedu Exp $	*/
/*	$NetBSD: ebus.c,v 1.24 2001/07/25 03:49:54 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.
 */

/*
 * UltraSPARC 5 and beyond ebus support.
 *
 * note that this driver is not complete:
 *	- ebus2 dma code is completely unwritten
 *	- interrupt establish is written and appears to work
 *	- bus map code is written and appears to work
 */

#ifdef DEBUG
#define	EDB_PROM	0x01
#define EDB_CHILD	0x02
#define	EDB_INTRMAP	0x04
#define EDB_BUSMAP	0x08
#define EDB_BUSDMA	0x10
#define EDB_INTR	0x20
int ebus_debug = 0x0;
#define DPRINTF(l, s)   do { if (ebus_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

#include <sys/param.h>
#include <sys/conf.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>

#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/pci/pcidevs.h>

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

int	ebus_match(struct device *, void *, void *);
void	ebus_attach(struct device *, struct device *, void *);

struct cfattach ebus_ca = {
	sizeof(struct ebus_softc), ebus_match, ebus_attach
};

struct cfdriver ebus_cd = {
	NULL, "ebus", DV_DULL
};

void	ebus_find_ino(struct ebus_softc *, struct ebus_attach_args *);
int	ebus_find_node(struct pci_attach_args *);

/*
 * here are our bus space and bus dma routines.
 */
static paddr_t ebus_bus_mmap(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    off_t, int, int);
static int _ebus_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    bus_size_t, int, bus_space_handle_t *);
bus_space_tag_t ebus_alloc_mem_tag(struct ebus_softc *, bus_space_tag_t);
bus_space_tag_t ebus_alloc_io_tag(struct ebus_softc *, bus_space_tag_t);
bus_space_tag_t _ebus_alloc_bus_tag(struct ebus_softc *sc, const char *,
    bus_space_tag_t, int);

int
ebus_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;
	char name[10];
	int node;

	/* Only attach if there's a PROM node. */
	node = PCITAG_NODE(pa->pa_tag);
	if (node == -1) return (0);

	/* Match a real ebus */
	OF_getprop(node, "name", &name, sizeof(name));
	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_EBUS &&
		strcmp(name, "ebus") == 0)
		return (1);

	/* Or a real RIO ebus */
	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_RIO_EBUS &&
		strcmp(name, "ebus") == 0)
		return (1);

	/* Or a fake ebus */
	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALTERA &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALTERA_EBUS &&
		strcmp(name, "ebus") == 0)
		return (1);

	/* Or a PCI-ISA bridge XXX I hope this is on-board. */
	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA) {
		return (1);
	}

	return (0);
}

/*
 * attach an ebus and all its children.  this code is modeled
 * after the sbus code which does similar things.
 */
void
ebus_attach(struct device *parent, struct device *self, void *aux)
{
	struct ebus_softc *sc = (struct ebus_softc *)self;
	struct pci_attach_args *pa = aux;
	struct ebus_attach_args eba;
	struct ebus_interrupt_map_mask *immp;
	int node, nmapmask, error;

	printf("\n");

	sc->sc_memtag = ebus_alloc_mem_tag(sc, pa->pa_memt);
	sc->sc_iotag = ebus_alloc_io_tag(sc, pa->pa_iot);
	sc->sc_dmatag = ebus_alloc_dma_tag(sc, pa->pa_dmat);

	node = PCITAG_NODE(pa->pa_tag);
	if (node == -1)
		panic("could not find ebus node");

	sc->sc_node = node;

	/*
	 * fill in our softc with information from the prom
	 */
	sc->sc_intmap = NULL;
	sc->sc_range = NULL;
	error = getprop(node, "interrupt-map",
			sizeof(struct ebus_interrupt_map),
			&sc->sc_nintmap, (void **)&sc->sc_intmap);
	switch (error) {
	case 0:
		immp = &sc->sc_intmapmask;
		error = getprop(node, "interrupt-map-mask",
			    sizeof(struct ebus_interrupt_map_mask), &nmapmask,
			    (void **)&immp);
		if (error)
			panic("could not get ebus interrupt-map-mask");
		if (nmapmask != 1)
			panic("ebus interrupt-map-mask is broken");
		break;
	case ENOENT:
		break;
	default:
		panic("ebus interrupt-map: error %d", error);
		break;
	}

	error = getprop(node, "ranges", sizeof(struct ebus_ranges),
	    &sc->sc_nrange, &sc->sc_range);
	if (error)
		panic("ebus ranges: error %d", error);

	/*
	 * now attach all our children
	 */
	DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
	for (node = firstchild(node); node; node = nextsibling(node)) {
		if (!checkstatus(node))
			continue;

		if (ebus_setup_attach_args(sc, node, &eba) != 0) {
			DPRINTF(EDB_CHILD,
			    ("ebus_attach: %s: incomplete\n",
			    getpropstring(node, "name")));
			continue;
		} else {
			DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
			    eba.ea_name));
			(void)config_found(self, &eba, ebus_print);
		}
		ebus_destroy_attach_args(&eba);
	}
}

int
ebus_setup_attach_args(struct ebus_softc *sc, int node,
    struct ebus_attach_args *ea)
{
	int	n, rv;

	bzero(ea, sizeof(struct ebus_attach_args));
	rv = getprop(node, "name", 1, &n, (void **)&ea->ea_name);
	if (rv != 0)
		return (rv);
	ea->ea_name[n] = '\0';

	ea->ea_node = node;
	ea->ea_memtag = sc->sc_memtag;
	ea->ea_iotag = sc->sc_iotag;
	ea->ea_dmatag = sc->sc_dmatag;

	rv = getprop(node, "reg", sizeof(struct ebus_regs), &ea->ea_nregs,
	    (void **)&ea->ea_regs);
	if (rv)
		return (rv);

	rv = getprop(node, "address", sizeof(u_int32_t), &ea->ea_nvaddrs,
	    (void **)&ea->ea_vaddrs);
	if (rv != ENOENT) {
		if (rv)
			return (rv);

		if (ea->ea_nregs != ea->ea_nvaddrs)
			printf("ebus loses: device %s: %d regs and %d addrs\n",
			    ea->ea_name, ea->ea_nregs, ea->ea_nvaddrs);
	} else
		ea->ea_nvaddrs = 0;

	if (getprop(node, "interrupts", sizeof(u_int32_t), &ea->ea_nintrs,
	    (void **)&ea->ea_intrs))
		ea->ea_nintrs = 0;
	else
		ebus_find_ino(sc, ea);

	return (0);
}

void
ebus_destroy_attach_args(struct ebus_attach_args *ea)
{
	if (ea->ea_name)
		free((void *)ea->ea_name, M_DEVBUF, 0);
	if (ea->ea_regs)
		free((void *)ea->ea_regs, M_DEVBUF, 0);
	if (ea->ea_intrs)
		free((void *)ea->ea_intrs, M_DEVBUF, 0);
	if (ea->ea_vaddrs)
		free((void *)ea->ea_vaddrs, M_DEVBUF, 0);
}

int
ebus_print(void *aux, const char *p)
{
	struct ebus_attach_args *ea = aux;
	int i;

	if (p)
		printf("\"%s\" at %s", ea->ea_name, p);
	for (i = 0; i < ea->ea_nregs; i++)
		printf("%s %x-%x", i == 0 ? " addr" : ",",
		    ea->ea_regs[i].lo,
		    ea->ea_regs[i].lo + ea->ea_regs[i].size - 1);
	for (i = 0; i < ea->ea_nintrs; i++)
		printf(" ivec 0x%x", ea->ea_intrs[i]);
	return (UNCONF);
}

/*
 * find the INO values for each interrupt and fill them in.
 *
 * for each "reg" property of this device, mask its hi and lo
 * values with the "interrupt-map-mask"'s hi/lo values, and also
 * mask the interrupt number with the interrupt mask.  search the
 * "interrupt-map" list for matching values of hi, lo and interrupt
 * to give the INO for this interrupt.
 */
void
ebus_find_ino(struct ebus_softc *sc, struct ebus_attach_args *ea)
{
	u_int32_t hi, lo, intr;
	int i, j, k;

	if (sc->sc_nintmap == 0) {
		for (i = 0; i < ea->ea_nintrs; i++) {
			OF_mapintr(ea->ea_node, &ea->ea_intrs[i],
				sizeof(ea->ea_intrs[0]),
				sizeof(ea->ea_intrs[0]));
		}
		return;
	}

	DPRINTF(EDB_INTRMAP,
	    ("ebus_find_ino: searching %d interrupts", ea->ea_nintrs));

	for (j = 0; j < ea->ea_nintrs; j++) {

		intr = ea->ea_intrs[j] & sc->sc_intmapmask.intr;

		DPRINTF(EDB_INTRMAP,
		    ("; intr %x masked to %x", ea->ea_intrs[j], intr));
		for (i = 0; i < ea->ea_nregs; i++) {
			hi = ea->ea_regs[i].hi & sc->sc_intmapmask.hi;
			lo = ea->ea_regs[i].lo & sc->sc_intmapmask.lo;

			DPRINTF(EDB_INTRMAP,
			    ("; reg hi.lo %08x.%08x masked to %08x.%08x",
			    ea->ea_regs[i].hi, ea->ea_regs[i].lo, hi, lo));
			for (k = 0; k < sc->sc_nintmap; k++) {
				DPRINTF(EDB_INTRMAP,
				    ("; checking hi.lo %08x.%08x intr %x",
				    sc->sc_intmap[k].hi, sc->sc_intmap[k].lo,
				    sc->sc_intmap[k].intr));
				if (hi == sc->sc_intmap[k].hi &&
				    lo == sc->sc_intmap[k].lo &&
				    intr == sc->sc_intmap[k].intr) {
					ea->ea_intrs[j] =
					    sc->sc_intmap[k].cintr;
					DPRINTF(EDB_INTRMAP,
					    ("; FOUND IT! changing to %d\n",
					    sc->sc_intmap[k].cintr));
					goto next_intr;
				}
			}
		}
next_intr:;
	}
}

bus_space_tag_t
ebus_alloc_mem_tag(struct ebus_softc *sc, bus_space_tag_t parent)
{
        return (_ebus_alloc_bus_tag(sc, "mem", parent,
            0x02));	/* 32-bit mem space (where's the #define???) */
}

bus_space_tag_t
ebus_alloc_io_tag(struct ebus_softc *sc, bus_space_tag_t parent)
{
        return (_ebus_alloc_bus_tag(sc, "io", parent,
            0x01));	/* IO space (where's the #define???) */
}

/*
 * bus space and bus dma below here
 */
bus_space_tag_t
_ebus_alloc_bus_tag(struct ebus_softc *sc, const char *name,
    bus_space_tag_t parent, int ss)
{
	struct sparc_bus_space_tag *bt;

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

	snprintf(bt->name, sizeof(bt->name), "%s_%s",
		sc->sc_dev.dv_xname, name);
	bt->cookie = sc;
	bt->parent = parent;
	bt->default_type = ss;
	bt->asi = parent->asi;
	bt->sasi = parent->sasi;
	bt->sparc_bus_map = _ebus_bus_map;
	bt->sparc_bus_mmap = ebus_bus_mmap;

	return (bt);
}

bus_dma_tag_t
ebus_alloc_dma_tag(struct ebus_softc *sc, bus_dma_tag_t pdt)
{
	bus_dma_tag_t dt;

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

	dt->_cookie = sc;
	dt->_parent = pdt;
	sc->sc_dmatag = dt;
	return (dt);
}

/*
 * bus space support.  <sparc64/dev/psychoreg.h> has a discussion
 * about PCI physical addresses, which also applies to ebus.
 */
static int
_ebus_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 ebus_softc *sc = t->cookie;
	struct ebus_ranges *range = sc->sc_range;
	bus_addr_t hi, lo;
	int i;

	DPRINTF(EDB_BUSMAP,
	    ("\n_ebus_bus_map: type %d off %016llx sz %x flags %d",
	    (int)t->default_type, (unsigned long long)offset, (int)size,
	    (int)flags));

	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
		printf("\n_ebus_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));
	}

	hi = offset >> 32UL;
	lo = offset & 0xffffffff;

	DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
	for (i = 0; i < sc->sc_nrange; i++) {
		bus_addr_t pciaddr;

		if (hi != range[i].child_hi)
			continue;
		if (lo < range[i].child_lo ||
		    (lo + size) > (range[i].child_lo + range[i].size))
			continue;

		if(((range[i].phys_hi >> 24) & 3) != t->default_type)
			continue;

		pciaddr = ((bus_addr_t)range[i].phys_mid << 32UL) |
				       range[i].phys_lo;
		pciaddr += lo;
		DPRINTF(EDB_BUSMAP,
		    ("\n_ebus_bus_map: mapping space %x paddr offset %qx "
		    "pciaddr %qx\n", (int)t->default_type,
		    (unsigned long long)offset, (unsigned long long)pciaddr));
                return ((*t->sparc_bus_map)(t, t0, pciaddr, size, flags, hp));
	}
	DPRINTF(EDB_BUSMAP, (": FAILED\n"));
	return (EINVAL);
}

static paddr_t
ebus_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 ebus_softc *sc = t->cookie;
	struct ebus_ranges *range = sc->sc_range;
	int i;

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

	t = t->parent;

	for (i = 0; i < sc->sc_nrange; i++) {
		bus_addr_t paddr = ((bus_addr_t)range[i].child_hi << 32) |
					        range[i].child_lo;

		if (offset != paddr)
			continue;

		DPRINTF(EDB_BUSMAP, ("\n_ebus_bus_mmap: mapping paddr %qx\n",
		    (unsigned long long)paddr));
		return ((*t->sparc_bus_mmap)(t, t0, paddr, off, prot, flags));
	}

	return (-1);
}


code:
/*	$OpenBSD: ebus_mainbus.c,v 1.8 2012/08/17 20:41:27 kettenis 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.
 */

#ifdef DEBUG
#define	EDB_PROM	0x01
#define EDB_CHILD	0x02
#define	EDB_INTRMAP	0x04
#define EDB_BUSMAP	0x08
#define EDB_BUSDMA	0x10
#define EDB_INTR	0x20
extern int ebus_debug;
#define DPRINTF(l, s)   do { if (ebus_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

#include <sys/param.h>
#include <sys/conf.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>

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

#include <dev/pci/pcivar.h>

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

extern struct cfdriver pyro_cd;

int	ebus_mainbus_match(struct device *, void *, void *);
void	ebus_mainbus_attach(struct device *, struct device *, void *);

struct cfattach ebus_mainbus_ca = {
	sizeof(struct ebus_softc), ebus_mainbus_match, ebus_mainbus_attach
};

int ebus_mainbus_bus_map(bus_space_tag_t, bus_space_tag_t,
    bus_addr_t, bus_size_t, int, bus_space_handle_t *);
void *ebus_mainbus_intr_establish(bus_space_tag_t, bus_space_tag_t,
    int, int, int, int (*)(void *), void *, const char *);
bus_space_tag_t ebus_alloc_bus_tag(struct ebus_softc *, bus_space_tag_t);
void ebus_mainbus_intr_ack(struct intrhand *);

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

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

void
ebus_mainbus_attach(struct device *parent, struct device *self, void *aux)
{
	struct ebus_softc *sc = (struct ebus_softc *)self;
	struct mainbus_attach_args *ma = aux;
	struct ebus_attach_args eba;
	struct ebus_interrupt_map_mask *immp;
	int node, nmapmask, error;
	struct pyro_softc *psc;
	int i, j;

	printf("\n");

	sc->sc_memtag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
	sc->sc_iotag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
	sc->sc_dmatag = ebus_alloc_dma_tag(sc, ma->ma_dmatag);

	sc->sc_node = node = ma->ma_node;

	/*
	 * fill in our softc with information from the prom
	 */
	sc->sc_intmap = NULL;
	sc->sc_range = NULL;
	error = getprop(node, "interrupt-map",
			sizeof(struct ebus_interrupt_map),
			&sc->sc_nintmap, (void **)&sc->sc_intmap);
	switch (error) {
	case 0:
		immp = &sc->sc_intmapmask;
		error = getprop(node, "interrupt-map-mask",
			    sizeof(struct ebus_interrupt_map_mask), &nmapmask,
			    (void **)&immp);
		if (error)
			panic("could not get ebus interrupt-map-mask");
		if (nmapmask != 1)
			panic("ebus interrupt-map-mask is broken");
		break;
	case ENOENT:
		break;
	default:
		panic("ebus interrupt-map: error %d", error);
		break;
	}

	/*
	 * Ebus interrupts may be connected to any of the PCI Express
	 * leafs.  Here we add the appropriate IGN to the interrupt
	 * mappings such that we can use it to distingish between
	 * intterupts connected to PCIE-A and PCIE-B.
	 */
	for (i = 0; i < sc->sc_nintmap; i++) {
		for (j = 0; j < pyro_cd.cd_ndevs; j++) {
			psc = pyro_cd.cd_devs[j];
			if (psc && psc->sc_node == sc->sc_intmap[i].cnode) {
				sc->sc_intmap[i].cintr |= psc->sc_ign;
				break;
			}
		}
	}

	error = getprop(node, "ranges", sizeof(struct ebus_mainbus_ranges),
	    &sc->sc_nrange, (void **)&sc->sc_range);
	if (error)
		panic("ebus ranges: error %d", error);

	/*
	 * now attach all our children
	 */
	DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
	for (node = firstchild(node); node; node = nextsibling(node)) {
		if (ebus_setup_attach_args(sc, node, &eba) != 0) {
			DPRINTF(EDB_CHILD,
			    ("ebus_mainbus_attach: %s: incomplete\n",
			    getpropstring(node, "name")));
			continue;
		} else {
			DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
			    eba.ea_name));
			(void)config_found(self, &eba, ebus_print);
		}
		ebus_destroy_attach_args(&eba);
	}
}

bus_space_tag_t
ebus_alloc_bus_tag(struct ebus_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 ebus bus tag");

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

	return (bt);
}

int
ebus_mainbus_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 ebus_softc *sc = t->cookie;
	struct ebus_mainbus_ranges *range;
	bus_addr_t hi, lo;
	int i;

	DPRINTF(EDB_BUSMAP,
	    ("\n_ebus_mainbus_bus_map: off %016llx sz %x flags %d",
	    (unsigned long long)offset, (int)size, (int)flags));

	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
		printf("\n_ebus_mainbus_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));
	}

	hi = offset >> 32UL;
	lo = offset & 0xffffffff;
	range = (struct ebus_mainbus_ranges *)sc->sc_range;

	DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
	for (i = 0; i < sc->sc_nrange; i++) {
		bus_addr_t addr;

		if (hi != range[i].child_hi)
			continue;
		if (lo < range[i].child_lo ||
		    (lo + size) > (range[i].child_lo + range[i].size))
			continue;

		addr = ((bus_addr_t)range[i].phys_hi << 32UL) |
				    range[i].phys_lo;
		addr += lo;
		DPRINTF(EDB_BUSMAP,
		    ("\n_ebus_mainbus_bus_map: paddr offset %qx addr %qx\n",
		    (unsigned long long)offset, (unsigned long long)addr));
                return ((*t->sparc_bus_map)(t, t0, addr, size, flags, hp));
	}
	DPRINTF(EDB_BUSMAP, (": FAILED\n"));
	return (EINVAL);
}

void *
ebus_mainbus_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 ebus_softc *sc = t->cookie;
	struct intrhand *ih = NULL;
	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
	int ino;

#ifdef SUN4V
	if (CPU_ISSUN4V) {
		struct upa_reg reg;
		u_int64_t devhandle, devino = INTINO(ihandle);
		u_int64_t sysino;
		int node = -1;
		int i, err;

		for (i = 0; i < sc->sc_nintmap; i++) {
			if (sc->sc_intmap[i].cintr == ihandle) {
				node = sc->sc_intmap[i].cnode;
				break;
			}
		}
		if (node == -1)
			return (NULL);

		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
			return (NULL);
		devhandle = (reg.ur_paddr >> 32) & 0x0fffffff;

		err = hv_intr_devino_to_sysino(devhandle, devino, &sysino);
		if (err != H_EOK)
			return (NULL);

		KASSERT(sysino == INTVEC(sysino));
		ih = bus_intr_allocate(t0, handler, arg, sysino, level,
		    NULL, NULL, what);
		if (ih == NULL)
			return (NULL);

		intr_establish(ih->ih_pil, ih);
		ih->ih_ack = ebus_mainbus_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);
	}
#endif

	ino = INTINO(ihandle);

	if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
		struct pyro_softc *psc = NULL;
		u_int64_t *imap, *iclr;
		int i;

		for (i = 0; i < pyro_cd.cd_ndevs; i++) {
			psc = pyro_cd.cd_devs[i];
			if (psc && psc->sc_ign == INTIGN(ihandle)) {
				break;
			}
		}
		if (psc == NULL)
			return (NULL);

		imap = bus_space_vaddr(psc->sc_bust, psc->sc_csrh) + 0x1000;
		iclr = bus_space_vaddr(psc->sc_bust, psc->sc_csrh) + 0x1400;
		intrmapptr = &imap[ino];
		intrclrptr = &iclr[ino];
		ino |= INTVEC(ihandle);
	}

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

	intr_establish(ih->ih_pil, ih);

	if (intrmapptr != NULL) {
		u_int64_t intrmap;

		intrmap = *intrmapptr;
		intrmap |= (1LL << 6);
		intrmap |= INTMAP_V;
		*intrmapptr = intrmap;
		intrmap = *intrmapptr;
		ih->ih_number |= intrmap & INTMAP_INR;
	}

	return (ih);
}

#ifdef SUN4V

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

#endif

license: unknown


code:
/*	$OpenBSD: ebusreg.h,v 1.5 2007/04/04 18:38:54 kettenis Exp $	*/
/*	$NetBSD: ebusreg.h,v 1.1 1999/06/04 13:29:13 mrg Exp $	*/

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

#ifndef _SPARC64_DEV_EBUSREG_H_
#define _SPARC64_DEV_EBUSREG_H_

/*
 * UltraSPARC `ebus'
 *
 * The `ebus' bus is designed to plug traditional PC-ISA devices into
 * an SPARC system with as few costs as possible, without sacrificing
 * to performance.  Typically, it is implemented in the PCIO IC from
 * SME, which also implements a `hme-compatible' PCI network device
 * (`network').  The ebus has 4 DMA channels, similar to the DMA seen
 * in the ESP SCSI DMA.
 *
 * Typical UltraSPARC systems have a NatSemi SuperIO IC to provide
 * serial ports for the keyboard and mouse (`se'), floppy disk
 * controller (`fdthree'), parallel port controller (`bpp') connected
 * to the ebus, and a PCI-IDE controller (connected directly to the
 * PCI bus, of course), as well as a Siemens Nixdorf SAB82532 dual
 * channel serial controller (`su' providing ttya and ttyb), an MK48T59
 * EEPROM/clock controller (also where the idprom, including the
 * ethernet address, is located), the audio system (`SUNW,CS4231', same
 * as other UltraSPARC and some SPARC systems), and other various
 * internal devices found on traditional SPARC systems such as the
 * `power', `flashprom', etc., devices.
 *
 * The ebus uses an interrupt mapping scheme similar to PCI, though
 * the actual structures are different.
 */

/*
 * ebus PROM structures
 */

struct ebus_regs {
	u_int32_t	hi;		/* high bits of physaddr */
	u_int32_t	lo;
	u_int32_t	size;
};

#define	EBUS_PADDR_FROM_REG(reg)	((((paddr_t)((reg)->hi)) << 32UL) | ((paddr_t)(reg)->lo))

struct ebus_ranges {
	u_int32_t	child_hi;	/* child high phys addr */
	u_int32_t	child_lo;	/* child low phys addr */
	u_int32_t	phys_hi;	/* parent high phys addr */
	u_int32_t	phys_mid;	/* parent mid phys addr */
	u_int32_t	phys_lo;	/* parent low phys addr */
	u_int32_t	size;
};

struct ebus_mainbus_ranges {
	u_int32_t	child_hi;
	u_int32_t	child_lo;
	u_int32_t	phys_hi;
	u_int32_t	phys_lo;
	u_int32_t	size;
};

struct ebus_interrupt_map {
	u_int32_t	hi;		/* high phys addr mask */
	u_int32_t	lo;		/* low phys addr mask */
	u_int32_t	intr;		/* interrupt mask */
	int32_t		cnode;		/* child node */
	u_int32_t	cintr;		/* child interrupt */
};

struct ebus_interrupt_map_mask {
	u_int32_t	hi;		/* high phys addr */
	u_int32_t	lo;		/* low phys addr */
	u_int32_t	intr;		/* interrupt */
};

/* EBUS dma registers */
#define	EBDMA_DCSR		0x0		/* control/status */
#define	EBDMA_DADDR		0x4		/* DMA address */
#define	EBDMA_DCNT		0x8		/* DMA count */

/* EBUS DMA control/status (EBDMA_DCSR) */
#define	EBDCSR_INT		0x00000001	/* interrupt pending */
#define	EBDCSR_ERR		0x00000002	/* error pending */
#define	EBDCSR_DRAIN		0x00000004	/* drain */
#define	EBDCSR_INTEN		0x00000010	/* interrupt enable */
#define	EBDCSR_RESET		0x00000080	/* reset */
#define	EBDCSR_WRITE		0x00000100	/* write */
#define	EBDCSR_DMAEN		0x00000200	/* dma enable */
#define	EBDCSR_CYC		0x00000400	/* cyc pending */
#define	EBDCSR_DIAGRD		0x00000800	/* diagnostic read done */
#define	EBDCSR_DIAGWR		0x00001000	/* diagnostic write done */
#define	EBDCSR_CNTEN		0x00002000	/* count enable */
#define	EBDCSR_TC		0x00004000	/* terminal count */
#define	EBDCSR_CSRDRNDIS	0x00010000	/* disable csr drain */
#define	EBDCSR_BURSTMASK	0x000c0000	/* burst size mask */
#define	EBDCSR_BURST_1		0x00080000	/* burst 1 */
#define	EBDCSR_BURST_4		0x00000000	/* burst 4 */
#define	EBDCSR_BURST_8		0x00040000	/* burst 8 */
#define	EBDCSR_BURST_16		0x000c0000	/* burst 16 */
#define	EBDCSR_DIAGEN		0x00100000	/* enable diagnostics */
#define	EBDCSR_ERRDIS		0x00400000	/* disable error pending */
#define	EBDCSR_TCIDIS		0x00800000	/* disable TCI */
#define	EBDCSR_NEXTEN		0x01000000	/* enable next */
#define	EBDCSR_DMAON		0x02000000	/* dma on */
#define	EBDCSR_A_LOADED		0x04000000	/* address loaded */
#define	EBDCSR_NA_LOADED	0x08000000	/* next address loaded */
#define	EBDCSR_DEVMASK		0xf0000000	/* device id mask */

#endif /* _SPARC64_DEV_EBUSREG_H_ */


code:
/*	$OpenBSD: ebusvar.h,v 1.7 2012/08/17 20:41:27 kettenis Exp $	*/
/*	$NetBSD: ebusvar.h,v 1.5 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.
 */

#ifndef _SPARC64_DEV_EBUSVAR_H_
#define _SPARC64_DEV_EBUSVAR_H_

/*
 * ebus arguments; ebus attaches to a pci, and devices attach
 * to the ebus.
 */

struct ebus_attach_args {
	char			*ea_name;	/* PROM name */
	int			ea_node;	/* PROM node */

	bus_space_tag_t		ea_memtag;
	bus_space_tag_t		ea_iotag;
	bus_dma_tag_t		ea_dmatag;

	struct ebus_regs	*ea_regs;	/* registers */
	u_int32_t		*ea_vaddrs;	/* virtual addrs */
	u_int32_t		*ea_intrs;	/* interrupts */

	int			ea_nregs;	/* number of them */
	int			ea_nvaddrs;
	int			ea_nintrs;
};

struct ebus_softc {
	struct device			sc_dev;

	int				sc_node;

	bus_space_tag_t			sc_memtag;	/* from pci */
	bus_space_tag_t			sc_iotag;	/* from pci */
	bus_dma_tag_t			sc_dmatag;	/* XXX */

	void				*sc_range;
	struct ebus_interrupt_map	*sc_intmap;
	struct ebus_interrupt_map_mask	sc_intmapmask;

	int				sc_nrange;	/* counters */
	int				sc_nintmap;
};

int ebus_setup_attach_args(struct ebus_softc *, int,
    struct ebus_attach_args *);
void ebus_destroy_attach_args(struct ebus_attach_args *);
int ebus_print(void *, const char *);

bus_dma_tag_t ebus_alloc_dma_tag(struct ebus_softc *, bus_dma_tag_t);

#define ebus_bus_map(t, bt, a, s, f, v, hp) \
	bus_space_map(t, a, s, f, hp)

#endif /* _SPARC64_DEV_EBUSVAR_H_ */


code:
/*	$OpenBSD: environ.c,v 1.1 2008/01/18 20:12:38 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 for environment device on Enterprise 3000/4000/5000/6000
 * and Enterprise 3500/4500/5500/6500.
 */

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

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

#include <sparc64/dev/fhcvar.h>

struct environ_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;

	struct ksensor		sc_sensor;
	struct ksensordev	sc_sensordev;
};

int	environ_match(struct device *, void *, void *);
void	environ_attach(struct device *, struct device *, void *);
void	environ_refresh(void *);

struct cfattach environ_ca = {
	sizeof(struct environ_softc), environ_match, environ_attach
};

struct cfdriver environ_cd = {
	NULL, "environ", DV_DULL
};

int
environ_match(struct device *parent, void *cf, void *aux)
{
	struct fhc_attach_args *fa = aux;

	if (strcmp("environment", fa->fa_name) == 0)
		return (1);
	return (0);
}

void
environ_attach(struct device *parent, struct device *self, void *aux)
{
	struct environ_softc *sc = (void *)self;
	struct fhc_attach_args *fa = aux;

	sc->sc_iot = fa->fa_bustag;
	if (fhc_bus_map(sc->sc_iot, fa->fa_reg[0].fbr_slot,
	    fa->fa_reg[0].fbr_offset, fa->fa_reg[0].fbr_size, 0,
	    &sc->sc_ioh)) {
		printf(": can't map registers\n");
		return;
	}

	/* First reading allegedly gives garbage. */
	bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);
	delay(30);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dv.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	sc->sc_sensor.type = SENSOR_TEMP;

	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);

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

	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

/*
 * Calibration table for 2nd generation CPU boards.
 */
int8_t environ_cpu2_temp[] = {
	-17, -16, -15, -14, -13, -12, -11, -10,
	 -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,
	 -1,   0,   1,   2,   3,   4,   5,   6,
	  7,   8,   9,  10,  11,  12,  13,  13,
	 14,  15,  16,  16,  17,  18,  18,  19,
	 20,  20,  21,  22,  22,  23,  24,  24,
	 25,  25,  26,  26,  27,  27,  28,  28,
	 29,  30,  30,  31,  31,  32,  32,  33,
	 33,  34,  34,  35,  35,  36,  36,  37,
	 37,  37,  38,  38,  39,  39,  40,  40,
	 41,  41,  42,  42,  43,  43,  43,  44,
	 44,  45,  45,  46,  46,  46,  47,  47,
	 48,  48,  49,  49,  50,  50,  50,  51,
	 51,  52,  52,  53,  53,  53,  54,  54,
	 55,  55,  56,  56,  56,  57,  57,  58,
	 58,  59,  59,  59,  60,  60,  61,  61,
	 62,  62,  63,  63,  63,  64,  64,  65,
	 65,  66,  66,  67,  67,  68,  68,  68,
	 69,  69,  70,  70,  71,  71,  72,  72,
	 73,  73,  74,  74,  75,  75,  76,  76,
	 77,  77,  78,  78,  79,  79,  80,  80,
	 81,  81,  82,  83,  83,  84,  84,  85,
	 85,  86,  87,  87,  88,  88,  89,  90,
	 90,  91,  92,  92,  93,  94,  94,  95,
	 96,  96,  97,  98,  99,  99, 100, 101,
	102, 103, 103, 104, 105, 106, 107, 108,
	109, 110
};

void
environ_refresh(void *arg)
{
	struct environ_softc *sc = arg;
	u_int8_t val;
	int8_t temp;

	val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);

	if (val == 0xff) {
		sc->sc_sensor.flags |= SENSOR_FINVALID;
		return;
	}

	if (val < sizeof(environ_cpu2_temp))
		temp = environ_cpu2_temp[val];
	else
		temp = 110;

	sc->sc_sensor.value = val * 1000000 + 273150000;
	sc->sc_sensor.flags &= ~SENSOR_FINVALID;
}


code:
/*	$OpenBSD: fb.c,v 1.25 2013/10/21 10:36:19 miod Exp $	*/
/*	$NetBSD: fb.c,v 1.23 1997/07/07 23:30:22 pk Exp $ */

/*
 * Copyright (c) 2002, 2004, 2008  Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fb.c	8.1 (Berkeley) 6/11/93
 */

/*
 * Common wsdisplay framebuffer drivers helpers.
 */

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

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

#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <machine/fbvar.h>

#include "wsdisplay.h"

/*
 * Sun specific color indexes.
 * Black is not really 7, but rather ~0; to fit within the 8 ANSI color
 * palette we are using on console, we pick (~0) & 0x07 instead.
 * This essentially swaps WSCOL_BLACK and WSCOL_WHITE.
 */
#define	WSCOL_SUN_WHITE		0
#define	WSCOL_SUN_BLACK		7

/*
 * emergency unblank code
 * XXX should be somewhat moved to wscons MI code
 */

void (*fb_burner)(void *, u_int, u_int);
void *fb_cookie;

void
fb_unblank()
{
	if (fb_burner != NULL)
		(*fb_burner)(fb_cookie, 1, 0);
}

#if NWSDISPLAY > 0

static int a2int(char *, int);
int	fb_get_console_metrics(int *, int *, int *, int *);
void	fb_initwsd(struct sunfb *);
void	fb_updatecursor(struct rasops_info *);

int	fb_alloc_screen(void *, const struct wsscreen_descr *, void **,
	    int *, int *, long *);
void	fb_free_screen(void *, void *);
int	fb_show_screen(void *, void *, int, void (*)(void *, int, int),
	    void *);
int	fb_load_font(void *, void *, struct wsdisplay_font *);
int	fb_list_font(void *, struct wsdisplay_font *);

void
fb_setsize(struct sunfb *sf, int def_depth, int def_width, int def_height,
    int node, int unused)
{
	int def_linebytes;

	sf->sf_depth = getpropint(node, "depth", def_depth);
	sf->sf_width = getpropint(node, "width", def_width);
	sf->sf_height = getpropint(node, "height", def_height);

	def_linebytes =
	    roundup(sf->sf_width, sf->sf_depth) * sf->sf_depth / 8;
	sf->sf_linebytes = getpropint(node, "linebytes", def_linebytes);

	/*
	 * XXX If we are configuring a board in a wider depth level
	 * than the mode it is currently operating in, the PROM will
	 * return a linebytes property tied to the current depth value,
	 * which is NOT what we are relying upon!
	 */
	if (sf->sf_linebytes < (sf->sf_width * sf->sf_depth) / 8)
		sf->sf_linebytes = def_linebytes;

	sf->sf_fbsize = sf->sf_height * sf->sf_linebytes;
}

static int
a2int(char *cp, int deflt)
{
	int i = 0;

	if (*cp == '\0')
		return (deflt);
	while (*cp != '\0')
		i = i * 10 + *cp++ - '0';
	return (i);
}

/* setup the embedded wsscreen_descr structure from rasops settings */
void
fb_initwsd(struct sunfb *sf)
{
	strlcpy(sf->sf_wsd.name, "std", sizeof(sf->sf_wsd.name));
	sf->sf_wsd.capabilities = sf->sf_ro.ri_caps;
	sf->sf_wsd.nrows = sf->sf_ro.ri_rows;
	sf->sf_wsd.ncols = sf->sf_ro.ri_cols;
	sf->sf_wsd.textops = &sf->sf_ro.ri_ops;
}

void
fb_updatecursor(struct rasops_info *ri)
{
	struct sunfb *sf = (struct sunfb *)ri->ri_hw;

	if (sf->sf_crowp != NULL)
		*sf->sf_crowp = ri->ri_crow;
	if (sf->sf_ccolp != NULL)
		*sf->sf_ccolp = ri->ri_ccol;
}

void
fbwscons_init(struct sunfb *sf, int flags, int isconsole)
{
	struct rasops_info *ri = &sf->sf_ro;
	int cols, rows, fw, fh, wt, wl;

	/* ri_hw and ri_bits must have already been setup by caller */
	ri->ri_flg = RI_FULLCLEAR | flags;
	ri->ri_depth = sf->sf_depth;
	ri->ri_stride = sf->sf_linebytes;
	ri->ri_width = sf->sf_width;
	ri->ri_height = sf->sf_height;

	rows = a2int(getpropstring(optionsnode, "screen-#rows"), 34);
	cols = a2int(getpropstring(optionsnode, "screen-#columns"), 80);

	/*
	 * If the framebuffer width is under 960 pixels, rasops will
	 * switch from the 12x22 font to the more adequate 8x16 font
	 * here.
	 * If we are the console device, we need to adjust two things:
	 * - the display row should be overrided from the current PROM
	 *   metrics, since it will not match the PROM reality anymore.
	 * - the screen needs to be cleared.
	 *
	 * However, to accomodate laptops with specific small fonts,
	 * it is necessary to compare the resolution with the actual
	 * font metrics.
	 */

	if (isconsole) {
		if (fb_get_console_metrics(&fw, &fh, &wt, &wl) != 0) {
			/*
			 * Assume a 12x22 prom font and a centered
			 * 80x34 console window.
			 */
			fw = 12; fh = 22;
			wt = wl = 0;
		} else {
			/*
			 * Make sure window-top and window-left
			 * values are consistent with the font metrics.
			 */
			if (wt <= 0 || wt > sf->sf_height - rows * fh ||
			    wl <= 0 || wl > sf->sf_width - cols * fw)
				wt = wl = 0;
		}
		if (wt == 0 /* || wl == 0 */) {
			ri->ri_flg |= RI_CENTER;

			/*
			 * Since the console window might not be
			 * centered (e.g. on a 1280x1024 vigra
			 * VS-12 frame buffer), have rasops
			 * clear the margins even if the screen is
			 * not cleared.
			 */
			ri->ri_flg |= RI_CLEARMARGINS;
		}

		if (ri->ri_wsfcookie != 0) {
			/* driver handles font issues. do nothing. */
		} else {
			/*
			 * If the PROM uses a different font than the
			 * one we are expecting it to use, or if the
			 * display is shorter than 960 pixels wide,
			 * we'll force a screen clear.
			 */
			if (fw != 12 || sf->sf_width < 12 * 80)
				ri->ri_flg |= RI_CLEAR | RI_CENTER;
		}
	} else {
		ri->ri_flg |= RI_CLEAR | RI_CENTER;
	}

	/* ifb(4) doesn't set ri_bits at the moment */
	if (ri->ri_bits == NULL)
		ri->ri_flg &= ~(RI_CLEAR | RI_CLEARMARGINS);

	rasops_init(ri, rows, cols);

	/*
	 * If this is the console display and there is no font change,
	 * adjust our terminal window to the position of the PROM
	 * window - in case it is not exactly centered.
	 */
	if ((ri->ri_flg & RI_CENTER) == 0) {
		/* code above made sure wt and wl are initialized */
		ri->ri_bits += wt * ri->ri_stride;
		if (ri->ri_depth >= 8)	/* for 15bpp to compute ok */
			ri->ri_bits += wl * ri->ri_pelbytes;
		else
			ri->ri_bits += (wl * ri->ri_depth) >> 3;

		ri->ri_xorigin = wl;
		ri->ri_yorigin = wt;
	}

	if (sf->sf_depth == 8) {
		/*
		 * If we are running with an indexed palette, compensate
		 * the swap of black and white through ri_devcmap.
		 */
		ri->ri_devcmap[WSCOL_SUN_BLACK] = 0;
		ri->ri_devcmap[WSCOL_SUN_WHITE] = 0xffffffff;
	} else if (sf->sf_depth > 8) {
		/*
		 * If we are running on a direct color frame buffer,
		 * make the ``normal'' white the same as the highlighted
		 * white.
		 */
		ri->ri_devcmap[WSCOL_WHITE] = ri->ri_devcmap[WSCOL_WHITE + 8];
	}
}

void
fbwscons_console_init(struct sunfb *sf, int row)
{
	struct rasops_info *ri = &sf->sf_ro;
	void *cookie;
	long defattr;

	if (romgetcursoraddr(&sf->sf_crowp, &sf->sf_ccolp))
		sf->sf_ccolp = sf->sf_crowp = NULL;
	if (sf->sf_ccolp != NULL)
		ri->ri_ccol = *sf->sf_ccolp;

	if (ri->ri_flg & RI_CLEAR) {
		/*
		 * If we have cleared the screen, this is because either
		 * we are not the console display, or the font has been
		 * changed.
		 * In this case, choose not to keep pointers to the PROM
		 * cursor position, as the values are likely to be inaccurate
		 * upon shutdown...
		 */
		sf->sf_crowp = sf->sf_ccolp = NULL;
		row = 0;
	}

	if (row < 0) /* no override */ {
		if (sf->sf_crowp != NULL)
			ri->ri_crow = *sf->sf_crowp;
		else
			/* assume last row */
			ri->ri_crow = ri->ri_rows - 1;
	} else {
		ri->ri_crow = row;
	}

	/*
	 * Scale back rows and columns if the font would not otherwise
	 * fit on this display. Without this we would panic later.
	 */
	if (ri->ri_crow >= ri->ri_rows)
		ri->ri_crow = ri->ri_rows - 1;
	if (ri->ri_ccol >= ri->ri_cols)
		ri->ri_ccol = ri->ri_cols - 1;

	/*
	 * Take care of updating the PROM cursor position as well if we can.
	 */
	if (ri->ri_updatecursor != NULL &&
	    (sf->sf_ccolp != NULL || sf->sf_crowp != NULL))
		ri->ri_updatecursor = fb_updatecursor;

	if (ri->ri_flg & RI_VCONS)
		cookie = ri->ri_active;
	else
		cookie = ri;

	if (ISSET(ri->ri_caps, WSSCREEN_WSCOLORS))
		ri->ri_ops.alloc_attr(cookie,
		    WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, &defattr);
	else
		ri->ri_ops.alloc_attr(cookie, 0, 0, 0, &defattr);

	fb_initwsd(sf);
	wsdisplay_cnattach(&sf->sf_wsd, cookie,
	    ri->ri_ccol, ri->ri_crow, defattr);
}

void
fbwscons_setcolormap(struct sunfb *sf,
    void (*setcolor)(void *, u_int, u_int8_t, u_int8_t, u_int8_t))
{
	int i;
	const u_char *color;

	if (sf->sf_depth <= 8 && setcolor != NULL) {
		for (i = 0; i < 16; i++) {
			color = &rasops_cmap[i * 3];
			setcolor(sf, i, color[0], color[1], color[2]);
		}
		for (i = 240; i < 256; i++) {
			color = &rasops_cmap[i * 3];
			setcolor(sf, i, color[0], color[1], color[2]);
		}
		/*
		 * Compensate for BoW default hardware palette: existing
		 * output (which we do not want to affect) is black on
		 * white with color index 0 being white and 0xff being
		 * black.
		 */
		setcolor(sf, WSCOL_SUN_WHITE, 0xff, 0xff, 0xff);
		setcolor(sf, 0xff ^ WSCOL_SUN_WHITE, 0, 0, 0);
		setcolor(sf, WSCOL_SUN_BLACK, 0, 0, 0);
		setcolor(sf, 0xff ^ (WSCOL_SUN_BLACK), 0xff, 0xff, 0xff);
	}
}

void
fbwscons_attach(struct sunfb *sf, struct wsdisplay_accessops *op, int isconsole)
{
	struct wsemuldisplaydev_attach_args waa;

	if (isconsole == 0) {
		/* done in wsdisplay_cnattach() earlier if console */
		fb_initwsd(sf);
	} else {
		/* remember screen burner routine */
		fb_burner = op->burn_screen;
		fb_cookie = sf;
	}

	/* plug common wsdisplay_accessops if necessary */
	if (op->alloc_screen == NULL) {
		op->alloc_screen = fb_alloc_screen;
		op->free_screen = fb_free_screen;
		op->show_screen = fb_show_screen;
	}
	if (op->load_font == NULL) {
		op->load_font = fb_load_font;
		op->list_font = fb_list_font;
	}

	sf->sf_scrlist[0] = &sf->sf_wsd;
	sf->sf_wsl.nscreens = 1;
	sf->sf_wsl.screens = (const struct wsscreen_descr **)sf->sf_scrlist;

	waa.console = isconsole;
	waa.scrdata = &sf->sf_wsl;
	waa.accessops = op;
	waa.accesscookie = sf;
	waa.defaultscreens = 0;
	config_found(&sf->sf_dev, &waa, wsemuldisplaydevprint);
}

/*
 * Common wsdisplay_accessops routines.
 */
int
fb_alloc_screen(void *v, const struct wsscreen_descr *type,
    void **cookiep, int *curxp, int *curyp, long *attrp)
{
	struct sunfb *sf = v;
	struct rasops_info *ri = &sf->sf_ro;
	void *cookie;

	if (sf->sf_nscreens > 0)
		return (ENOMEM);

	if (ri->ri_flg & RI_VCONS)
		cookie = ri->ri_active;
	else
		cookie = ri;

	*cookiep = cookie;
	*curyp = 0;
	*curxp = 0;
	if (ISSET(ri->ri_caps, WSSCREEN_WSCOLORS))
		ri->ri_ops.alloc_attr(cookie,
		    WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, attrp);
	else
		ri->ri_ops.alloc_attr(cookie, 0, 0, 0, attrp);
	sf->sf_nscreens++;
	return (0);
}

void
fb_free_screen(void *v, void *cookie)
{
	struct sunfb *sf = v;

	sf->sf_nscreens--;
}

int
fb_show_screen(void *v, void *cookie, int waitok, void (*cb)(void *, int, int),
    void *cbarg)
{
	return (0);
}

int
fb_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
{
	struct sunfb *sf = v;
	struct rasops_info *ri = &sf->sf_ro;

	return rasops_load_font(ri, emulcookie, font);
}

int
fb_list_font(void *v, struct wsdisplay_font *font)
{
	struct sunfb *sf = v;
	struct rasops_info *ri = &sf->sf_ro;

	return rasops_list_font(ri, font);
}

int
fb_get_console_metrics(int *fontwidth, int *fontheight, int *wtop, int *wleft)
{
	cell_t romheight, romwidth, windowtop, windowleft;

	/*
	 * Get the PROM font metrics and address
	 */
	OF_interpret("stdout @ is my-self "
	    "addr char-height addr char-width "
	    "addr window-top addr window-left",
	    4, &windowleft, &windowtop, &romwidth, &romheight);

	if (romheight == 0 || romwidth == 0 ||
	    windowtop == 0 || windowleft == 0)
		return (1);

	*fontwidth = (int)*(uint64_t *)romwidth;
	*fontheight = (int)*(uint64_t *)romheight;
	*wtop = (int)*(uint64_t *)windowtop;
	*wleft = (int)*(uint64_t *)windowleft;

	return (0);
}

#endif	/* NWSDISPLAY */