code:
/*	$OpenBSD: dma.c,v 1.31 2015/03/30 20:30:22 miod Exp $	*/
/*	$NetBSD: dma.c,v 1.46 1997/08/27 11:24:16 bouyer Exp $ */

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

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

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

#include <sparc/sparc/cpuvar.h>

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

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

#include <uvm/uvm_extern.h>

#include <dev/ic/ncr53c9xreg.h>
#include <dev/ic/ncr53c9xvar.h>

#include <sparc/dev/sbusvar.h>
#include <sparc/dev/dmareg.h>
#include <sparc/dev/dmavar.h>
#include <sparc/dev/espvar.h>

int dmaprint(void *, const char *);
void dmaattach(struct device *, struct device *, void *);
int dmamatch(struct device *, void *, void *);
void dma_reset(struct dma_softc *, int);
void espdma_reset(struct dma_softc *);
int ledmamatch(struct device *, void *, void *);
void ledma_reset(struct dma_softc *);
void dma_enintr(struct dma_softc *);
int dma_isintr(struct dma_softc *);
int espdmaintr(struct dma_softc *);
int ledmaintr(struct dma_softc *);
int dma_setup(struct dma_softc *, caddr_t *, size_t *,
			     int, size_t *);
void dma_go(struct dma_softc *);

struct cfattach dma_ca = {
	sizeof(struct dma_softc), dmamatch, dmaattach
};

struct cfdriver dma_cd = {
	NULL, "dma", DV_DULL
};

struct cfattach ledma_ca = {
	sizeof(struct dma_softc), ledmamatch, dmaattach
};

struct cfdriver ledma_cd = {
	NULL, "ledma", DV_DULL
};

int
dmaprint(aux, name)
	void *aux;
	const char *name;
{
	struct confargs *ca = aux;

	if (name)
		printf("%s at %s", ca->ca_ra.ra_name, name);
	printf(" offset 0x%x", ca->ca_offset);
	return (UNCONF);
}

int
dmamatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	if (strcmp(cf->cf_driver->cd_name, ra->ra_name) &&
	    strcmp("espdma", ra->ra_name))
		return (0);
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (ca->ca_bustype == BUS_SBUS) {
		if (!sbus_testdma((struct sbus_softc *)parent, ca))
			return (0);
		return (1);
	}
#endif
	ra->ra_len = NBPG;
	return (probeget(ra->ra_vaddr, 4) != -1);
}

/*
 * Attach all the sub-devices we can find
 */
void
dmaattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct confargs *ca = aux;
	struct dma_softc *sc = (void *)self;
	int devnode;
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	int node;
	struct confargs oca;
	char *name;
#endif

	if (ca->ca_ra.ra_vaddr == NULL || ca->ca_ra.ra_nvaddrs == 0)
		ca->ca_ra.ra_vaddr =
		    mapiodev(ca->ca_ra.ra_reg, 0, ca->ca_ra.ra_len);

	sc->sc_regs = (struct dma_regs *) ca->ca_ra.ra_vaddr;
	devnode = ca->ca_ra.ra_node;

	/*
	 * If we're a ledma, check to see what cable type is currently
	 * active and set the appropriate bit in the ledma csr so that
	 * it gets used. If we didn't netboot, the PROM won't have the
	 * "cable-selection" property; default to TP and then the user
	 * can change it via a "link0" option to ifconfig.
	 */
	if (strcmp(ca->ca_ra.ra_name, "ledma") == 0) {
		char *cabletype = getpropstring(devnode, "cable-selection");
		if (strcmp(cabletype, "tpe") == 0) {
			sc->sc_regs->csr |= E_TP_AUI;
			sc->sc_defaultmedia = IFM_ETHER | IFM_10_T;
		} else if (strcmp(cabletype, "aui") == 0) {
			sc->sc_regs->csr &= ~E_TP_AUI;
			sc->sc_defaultmedia = IFM_ETHER | IFM_10_5;
		} else {
			/* assume TP if nothing there */
			sc->sc_regs->csr |= E_TP_AUI;
			sc->sc_defaultmedia = IFM_ETHER | IFM_AUTO;
		}
		delay(20000);	/* manual says we need 20ms delay */
	}

	/*
	 * Get transfer burst size from PROM and plug it into the
	 * controller registers. This is needed on the Sun4m; do
	 * others need it too?
	 */
	if (CPU_ISSUN4M) {
		int sbusburst = ((struct sbus_softc *)parent)->sc_burst;
		if (sbusburst == 0)
			sbusburst = SBUS_BURST_32 - 1; /* 1->16 */

		sc->sc_burst = getpropint(devnode, "burst-sizes", -1);
		if (sc->sc_burst == -1)
			/* take SBus burst sizes */
			sc->sc_burst = sbusburst;

		/* Clamp at parent's burst sizes */
		sc->sc_burst &= sbusburst;
	}

	printf(": rev ");
	sc->sc_rev = sc->sc_regs->csr & D_DEV_ID;
	switch (sc->sc_rev) {
	case DMAREV_0:
		printf("0");
		break;
	case DMAREV_ESC:
		printf("esc");
		break;
	case DMAREV_1:
		printf("1");
		break;
	case DMAREV_PLUS:
		printf("1+");
		break;
	case DMAREV_2:
		printf("2");
		break;
	case DMAREV_HME:
		printf("fas");
		break;
	default:
		printf("unknown (0x%x)", sc->sc_rev);
	}
	printf("\n");

	/* indirect functions */
	dma_setuphandlers(sc);

	if (CPU_ISSUN4)
		goto espsearch;

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	/* Propagate bootpath */
	if (ca->ca_ra.ra_bp != NULL &&
	    (strcmp(ca->ca_ra.ra_bp->name, "espdma") == 0 ||
	     strcmp(ca->ca_ra.ra_bp->name, "dma") == 0 ||
	     strcmp(ca->ca_ra.ra_bp->name, "ledma") == 0))
		oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
	else
		oca.ca_ra.ra_bp = NULL;

	/* search through children */
	node = firstchild(devnode);
	if (node != 0) {
		do {
			name = getpropstring(node, "name");
			if (!romprop(&oca.ca_ra, name, node))
				continue;

			sbus_translate(parent, &oca);
			oca.ca_bustype = BUS_SBUS;
			oca.ca_dmat = ca->ca_dmat;
			config_found(&sc->sc_dev, (void *)&oca, dmaprint);
		} while ((node = nextsibling(node)) != 0);
	} else
#endif /* SUN4C || SUN4D || SUN4E || SUN4M */

	if (strcmp(ca->ca_ra.ra_name, "dma") == 0) {
espsearch:
		/*
		 * find the ESP by poking around the esp device structures
		 *
		 * What happens here is that if the esp driver has not been
		 * configured, then this returns a NULL pointer. Then when the
		 * esp actually gets configured, it does the opposing test, and
		 * if the sc->sc_dma field in its softc is NULL, then tries to
		 * find the matching dma driver.
		 *
		 */
		sc->sc_esp = (struct esp_softc *)
			     getdevunit("esp", sc->sc_dev.dv_unit);

		/*
		 * and a back pointer to us, for DMA
		 */
		if (sc->sc_esp)
			sc->sc_esp->sc_dma = sc;
	}
}

void
dma_setuphandlers(struct dma_softc *sc)
{
	if (sc->sc_dev.dv_cfdata == NULL ||	/* happens on SUNW,fas */
	    sc->sc_dev.dv_cfdata->cf_attach == &dma_ca) {
		sc->reset = espdma_reset;
		sc->intr = espdmaintr;
	} else {
		sc->reset = ledma_reset;
		sc->intr = ledmaintr;
	}
	sc->enintr = dma_enintr;
	sc->isintr = dma_isintr;
	sc->setup = dma_setup;
	sc->go = dma_go;
}

#define DMAWAIT(SC, COND, MSG, DONTPANIC) do if (COND) {		\
	int count = 500000;						\
	while ((COND) && --count > 0) DELAY(1);				\
	if (count == 0) {						\
		printf("%s: line %d: CSR = 0x%lx\n", __FILE__, __LINE__, \
			(SC)->sc_regs->csr);				\
		if (DONTPANIC)						\
			printf(MSG);					\
		else							\
			panic(MSG);					\
	}								\
} while (0)

#define DMA_DRAIN(sc, dontpanic) do {					\
	/*								\
	 * DMA rev0 & rev1: we are not allowed to touch the DMA "flush"	\
	 *     and "drain" bits while it is still thinking about a	\
	 *     request.							\
	 * other revs: D_ESC_R_PEND bit reads as 0			\
	 */								\
	DMAWAIT(sc, sc->sc_regs->csr & D_ESC_R_PEND, "R_PEND", dontpanic);\
	if (sc->sc_rev != DMAREV_HME) {					\
		/*							\
		 * Select drain bit based on revision			\
		 * also clears errors and D_TC flag			\
		 */							\
		if (sc->sc_rev == DMAREV_1 || sc->sc_rev == DMAREV_0)	\
			DMACSR(sc) |= D_ESC_DRAIN;			\
		else							\
			DMACSR(sc) |= D_INVALIDATE;			\
	}								\
	/*								\
	 * Wait for draining to finish					\
	 *  rev0 & rev1 call this PACKCNT				\
	 */								\
	DMAWAIT(sc, sc->sc_regs->csr & D_DRAINING, "DRAINING", dontpanic);\
} while(0)

#define DMA_FLUSH(sc, dontpanic) do {					\
	int csr;							\
	/*								\
	 * DMA rev0 & rev1: we are not allowed to touch the DMA "flush"	\
	 *     and "drain" bits while it is still thinking about a	\
	 *     request.							\
	 * other revs: D_ESC_R_PEND bit reads as 0			\
	 */								\
	DMAWAIT(sc, sc->sc_regs->csr & D_ESC_R_PEND, "R_PEND", dontpanic);\
	csr = DMACSR(sc);						\
	csr &= ~(D_WRITE|D_EN_DMA);	/* no-ops on ENET */		\
	csr |= D_INVALIDATE;		/* XXX FAS ? */			\
	DMACSR(sc) = csr;						\
} while(0)

void
dma_reset(sc, isledma)
	struct dma_softc *sc;
	int isledma;
{
	int csr;

	DMA_FLUSH(sc, 1);
	csr = DMACSR(sc);

	if (sc->sc_rev == DMAREV_HME)
		DMACSR(sc) = csr | D_HW_RESET_FAS366;

	csr |= D_RESET;				/* reset DMA */
	DMACSR(sc) = csr;
	DELAY(200);				/* > 10 SBus clocks(?) */

	/*DMAWAIT1(sc); why was this here? */
	DMACSR(sc) &= ~D_RESET;			/* de-assert reset line */
	DELAY(5);

	csr = DMACSR(sc);
	csr |= D_INT_EN;			/* enable interrupts */
	if (sc->sc_rev > DMAREV_1 && isledma == 0) {
		if (sc->sc_rev == DMAREV_HME)
			csr |= D_TWO_CYCLE;
		else
			csr |= D_FASTER;
	}

	switch (sc->sc_rev) {
	case DMAREV_HME:
	case DMAREV_2:
		csr &= ~L64854_BURST_SIZE; /* must clear first */
		if (sc->sc_burst & SBUS_BURST_32) {
			csr |= L64854_BURST_32;
		} else if (sc->sc_burst & SBUS_BURST_16) {
			csr |= L64854_BURST_16;
		} else {
			csr |= L64854_BURST_0;
		}
		break;
	case DMAREV_ESC:
		csr |= D_ESC_AUTODRAIN;	/* Auto-drain */
		if (sc->sc_burst & SBUS_BURST_32) {
			csr &= ~D_ESC_BURST;
		} else
			csr |= D_ESC_BURST;
		break;
	default:
		break;
	}
	DMACSR(sc) = csr;

	if (sc->sc_rev == DMAREV_HME) {
		DMADDR(sc) = 0;
		sc->sc_dmactl = csr;
	}

	sc->sc_active = 0;			/* and of course we aren't */
}

void
espdma_reset(sc)
	struct dma_softc *sc;
{
	dma_reset(sc, 0);
}

int
ledmamatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

        if (strcmp(cf->cf_driver->cd_name, ra->ra_name))
		return (0);
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (!sbus_testdma((struct sbus_softc *)parent, ca))
		return(0);
#endif
	return (1);
}

void
ledma_reset(sc)
	struct dma_softc *sc;
{
	dma_reset(sc, 1);
}

void
dma_enintr(sc)
	struct dma_softc *sc;
{
	sc->sc_regs->csr |= D_INT_EN;
}

int
dma_isintr(sc)
	struct dma_softc *sc;
{
	return (sc->sc_regs->csr & (D_INT_PEND|D_ERR_PEND));
}

#define DMAMAX(a)	(0x01000000 - ((a) & 0x00ffffff))

/*
 * setup a dma transfer
 */
int
dma_setup(sc, addr, len, datain, dmasize)
	struct dma_softc *sc;
	caddr_t *addr;
	size_t *len;
	int datain;
	size_t *dmasize;	/* IN-OUT */
{
	u_long csr;

	DMA_FLUSH(sc, 0);

#if 0
	DMACSR(sc) &= ~D_INT_EN;
#endif
	sc->sc_dmaaddr = addr;
	sc->sc_dmalen = len;

	NCR_DMA(("%s: start %zd@%p,%d\n", sc->sc_dev.dv_xname,
		*sc->sc_dmalen, *sc->sc_dmaaddr, datain ? 1 : 0));

	/*
	 * the rules say we cannot transfer more than the limit
	 * of this DMA chip (64k for old and 16Mb for new),
	 * and we cannot cross a 16Mb boundary.
	 */
	*dmasize = sc->sc_dmasize =
		min(*dmasize, DMAMAX((size_t) *sc->sc_dmaaddr));

	NCR_DMA(("dma_setup: dmasize = %zd\n", sc->sc_dmasize));

	/*
	 * XXX what length?
	 */
	if (sc->sc_rev == DMAREV_HME) {
		DMACSR(sc) = sc->sc_dmactl | L64854_RESET;
		DMACSR(sc) = sc->sc_dmactl;
		DMACNT(sc) = *dmasize;
	}

	/* Program the DMA address */
	if (CPU_ISSUN4M && sc->sc_dmasize) {
		/*
		 * Use dvma mapin routines to map the buffer into DVMA space.
		 */
		sc->sc_dvmaaddr = *sc->sc_dmaaddr;
		sc->sc_dvmakaddr = kdvma_mapin(sc->sc_dvmaaddr,
					       sc->sc_dmasize, 0);
		if (sc->sc_dvmakaddr == NULL)
			panic("dma: cannot allocate DVMA address");
		DMADDR(sc) = sc->sc_dvmakaddr;
	} else
		DMADDR(sc) = *sc->sc_dmaaddr;

	if (sc->sc_rev == DMAREV_ESC) {
		/* DMA ESC chip bug work-around */
		long bcnt = sc->sc_dmasize;
		long eaddr = bcnt + (long)*sc->sc_dmaaddr;
		if ((eaddr & PGOFSET) != 0)
			bcnt = round_page(bcnt);
		DMACNT(sc) = bcnt;
	}
	/* Setup DMA control register */
	csr = DMACSR(sc);
	if (datain)
		csr |= D_WRITE;
	else
		csr &= ~D_WRITE;
	csr |= D_INT_EN;

	if (sc->sc_rev == DMAREV_HME) {
		csr |= (D_DSBL_SCSI_DRN | D_EN_DMA);
	}

	DMACSR(sc) = csr;

	return 0;
}

void
dma_go(sc)
	struct dma_softc *sc;
{

	/* Start DMA */
	DMACSR(sc) |= D_EN_DMA;
	sc->sc_active = 1;
}

/*
 * Pseudo (chained) interrupt from the esp driver to kick the
 * current running DMA transfer. I am replying on espintr() to
 * pickup and clean errors for now
 *
 * return 1 if it was a DMA continue.
 */
int
espdmaintr(sc)
	struct dma_softc *sc;
{
	struct ncr53c9x_softc *nsc = &sc->sc_esp->sc_ncr53c9x;
	int trans, resid;
	u_long csr;

	csr = DMACSR(sc);

	NCR_DMA(("%s: intr: addr %p, csr %lb\n", sc->sc_dev.dv_xname,
		 DMADDR(sc), csr, DDMACSR_BITS));

	if (csr & (D_ERR_PEND|D_SLAVE_ERR)) {
		printf("%s: error: csr=%lb\n", sc->sc_dev.dv_xname,
			csr, DDMACSR_BITS);
		DMACSR(sc) &= ~D_EN_DMA;	/* Stop DMA */
		/* Invalidate the queue; SLAVE_ERR bit is write-to-clear */
		DMACSR(sc) |= D_INVALIDATE|D_SLAVE_ERR;
		return (-1);
	}

	/* This is an "assertion" :) */
	if (sc->sc_active == 0)
		panic("dmaintr: DMA wasn't active");

	DMA_DRAIN(sc, 0);

	/* DMA has stopped */
	DMACSR(sc) &= ~D_EN_DMA;
	sc->sc_active = 0;

	if (sc->sc_dmasize == 0) {
		/* A "Transfer Pad" operation completed */
		NCR_DMA(("dmaintr: discarded %d bytes (tcl=%d, tcm=%d)\n",
			NCR_READ_REG(nsc, NCR_TCL) |
				(NCR_READ_REG(nsc, NCR_TCM) << 8),
			NCR_READ_REG(nsc, NCR_TCL),
			NCR_READ_REG(nsc, NCR_TCM)));
		return 0;
	}

	resid = 0;
	/*
	 * If a transfer onto the SCSI bus gets interrupted by the device
	 * (e.g. for a SAVEPOINTER message), the data in the FIFO counts
	 * as residual since the ESP counter registers get decremented as
	 * bytes are clocked into the FIFO.
	 */
	if (!(csr & D_WRITE) &&
	    (resid = (NCR_READ_REG(nsc, NCR_FFLAG) & NCRFIFO_FF)) != 0) {
		NCR_DMA(("dmaintr: empty esp FIFO of %d ", resid));
	}

	if ((nsc->sc_espstat & NCRSTAT_TC) == 0) {
		/*
		 * `Terminal count' is off, so read the residue
		 * out of the ESP counter registers.
		 */
		resid += (NCR_READ_REG(nsc, NCR_TCL) |
			  (NCR_READ_REG(nsc, NCR_TCM) << 8) |
			   ((nsc->sc_cfg2 & NCRCFG2_FE)
				? (NCR_READ_REG(nsc, NCR_TCH) << 16)
				: 0));

		if (resid == 0 && sc->sc_dmasize == 65536 &&
		    (nsc->sc_cfg2 & NCRCFG2_FE) == 0)
			/* A transfer of 64K is encoded as `TCL=TCM=0' */
			resid = 65536;
	}

	trans = sc->sc_dmasize - resid;
	if (trans < 0) {			/* transferred < 0 ? */
#if 0
		/*
		 * This situation can happen in perfectly normal operation
		 * if the ESP is reselected while using DMA to select
		 * another target.  As such, don't print the warning.
		 */
		printf("%s: xfer (%d) > req (%d)\n",
		    sc->sc_dev.dv_xname, trans, sc->sc_dmasize);
#endif
		trans = sc->sc_dmasize;
	}

	NCR_DMA(("dmaintr: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n",
		NCR_READ_REG(nsc, NCR_TCL),
		NCR_READ_REG(nsc, NCR_TCM),
		(nsc->sc_cfg2 & NCRCFG2_FE)
			? NCR_READ_REG(nsc, NCR_TCH) : 0,
		trans, resid));

	if (csr & D_WRITE)
		cpuinfo.cache_flush(*sc->sc_dmaaddr, trans);

	if (CPU_ISSUN4M && sc->sc_dvmakaddr)
		dvma_mapout((vaddr_t)sc->sc_dvmakaddr,
			    (vaddr_t)sc->sc_dvmaaddr, sc->sc_dmasize);

	*sc->sc_dmalen -= trans;
	*sc->sc_dmaaddr += trans;

#if 0	/* this is not normal operation just yet */
	if (*sc->sc_dmalen == 0 ||
	    nsc->sc_phase != nsc->sc_prevphase)
		return 0;

	/* and again */
	dma_start(sc, sc->sc_dmaaddr, sc->sc_dmalen, DMACSR(sc) & D_WRITE);
	return 1;
#endif
	return 0;
}

/*
 * Pseudo (chained) interrupt from the le driver to handle DMA
 * errors.
 *
 * XXX: untested
 */
int
ledmaintr(sc)
	struct dma_softc *sc;
{
	u_long csr;

	csr = DMACSR(sc);

	if (csr & D_ERR_PEND) {
		DMACSR(sc) &= ~D_EN_DMA;	/* Stop DMA */
		DMACSR(sc) |= D_INVALIDATE;
		printf("%s: error: csr=%lb\n", sc->sc_dev.dv_xname,
			csr, DDMACSR_BITS);
		DMA_RESET(sc);
	}
	return 1;
}


code:
/*	$OpenBSD: dmareg.h,v 1.7 2005/02/27 22:01:04 miod Exp $	*/
/*	$NetBSD: dmareg.h,v 1.10 1996/11/28 09:37:34 pk Exp $ */

/*
 * Copyright (c) 1994 Peter Galbavy.  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 <dev/ic/lsi64854reg.h>

struct dma_regs {
	volatile u_long		csr;		/* DMA CSR */
	volatile caddr_t	addr;
	volatile u_long		bcnt;		/* DMA COUNT (in u_longs) */
	volatile u_long		test;		/* DMA TEST (in u_longs) */
#define en_testcsr	addr			/* enet registers overlap */
#define en_cachev	bcnt
#define en_bar		test
};

/*
 * PROM-reported DMA burst sizes for the SBus
 */
#define SBUS_BURST_1	0x1
#define SBUS_BURST_2	0x2
#define SBUS_BURST_4	0x4
#define SBUS_BURST_8	0x8
#define SBUS_BURST_16	0x10
#define SBUS_BURST_32	0x20
#define SBUS_BURST_64	0x40

license: unknown


code:
/*	$OpenBSD: dmavar.h,v 1.8 2014/07/28 18:31:39 miod Exp $	*/
/*	$NetBSD: dmavar.h,v 1.11 1996/11/27 21:49:53 pk Exp $ */

/*
 * Copyright (c) 1994 Peter Galbavy.  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 dma_softc {
	struct device sc_dev;			/* us as a device */
	struct esp_softc *sc_esp;		/* my scsi */
	struct le_softc *sc_le;			/* my Ethernet */
	struct dma_regs *sc_regs;		/* the registers */
	int	sc_active;			/* DMA active ? */
	u_int	sc_rev;				/* revision */
	int	sc_burst;			/* DVMA burst size in effect */
	caddr_t	sc_dvmakaddr;			/* DVMA cookies */
	caddr_t	sc_dvmaaddr;			/*		*/
	size_t	sc_dmasize;
	caddr_t	*sc_dmaaddr;
	size_t  *sc_dmalen;
	void (*reset)(struct dma_softc *);	/* reset routine */
	void (*enintr)(struct dma_softc *);	/* enable interrupts */
	int (*isintr)(struct dma_softc *);	/* interrupt ? */
	int (*intr)(struct dma_softc *);	/* interrupt ! */
	int (*setup)(struct dma_softc *, caddr_t *, size_t *, int, size_t *);
	void (*go)(struct dma_softc *);
	u_int	sc_dmactl;
	int	sc_defaultmedia;		/* default Ethernet media */
};

#define DMACSR(sc)	(sc->sc_regs->csr)
#define DMADDR(sc)	(sc->sc_regs->addr)
#define DMACNT(sc)	(sc->sc_regs->bcnt)

/* DMA engine functions */
#define DMA_ENINTR(r)		(((r)->enintr)(r))
#define DMA_ISINTR(r)		(((r)->isintr)(r))
#define DMA_RESET(r)		(((r)->reset)(r))
#define DMA_INTR(r)		(((r)->intr)(r))
#define DMA_ISACTIVE(r)		((r)->sc_active)
#define DMA_SETUP(a, b, c, d, e)	(((a)->setup)(a, b, c, d, e))
#define DMA_GO(r)		(((r)->go)(r))

void	dma_setuphandlers(struct dma_softc *);


code:
/*	$OpenBSD: esp.c,v 1.36 2015/03/29 15:41:15 miod Exp $	*/
/*	$NetBSD: esp.c,v 1.69 1997/08/27 11:24:18 bouyer Exp $	*/

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

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

/*
 * Copyright (c) 1994 Peter Galbavy
 * Copyright (c) 1995 Paul Kranenburg
 * 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.
 */

/*
 * Based on aic6360 by Jarle Greipsland
 *
 * Acknowledgements: Many of the algorithms used in this driver are
 * inspired by the work of Julian Elischer (julian@tfs.com) and
 * Charles Hannum (mycroft@duality.gnu.ai.mit.edu).  Thanks a million!
 */

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

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

#include <uvm/uvm_extern.h>

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

#include <dev/ic/ncr53c9xreg.h>
#include <dev/ic/ncr53c9xvar.h>

#include <sparc/dev/sbusvar.h>
#include <sparc/dev/dmareg.h>
#include <sparc/dev/dmavar.h>
#include <sparc/dev/espvar.h>

void	espattach(struct device *, struct device *, void *);
int	espmatch(struct device *, void *, void *);

/* Linkup to the rest of the kernel */
struct cfattach esp_ca = {
	sizeof(struct esp_softc), espmatch, espattach
};

/*
 * Functions and the switch for the MI code.
 */
u_char	esp_read_reg(struct ncr53c9x_softc *, int);
void	esp_write_reg(struct ncr53c9x_softc *, int, u_char);
u_char	esp_rdreg1(struct ncr53c9x_softc *, int);
void	esp_wrreg1(struct ncr53c9x_softc *, int, u_char);
int	esp_dma_isintr(struct ncr53c9x_softc *);
void	esp_dma_reset(struct ncr53c9x_softc *);
int	esp_dma_intr(struct ncr53c9x_softc *);
int	esp_dma_setup(struct ncr53c9x_softc *, caddr_t *,
	    size_t *, int, size_t *);
void	esp_dma_go(struct ncr53c9x_softc *);
void	esp_dma_stop(struct ncr53c9x_softc *);
int	esp_dma_isactive(struct ncr53c9x_softc *);

struct ncr53c9x_glue esp_glue = {
	esp_read_reg,
	esp_write_reg,
	esp_dma_isintr,
	esp_dma_reset,
	esp_dma_intr,
	esp_dma_setup,
	esp_dma_go,
	esp_dma_stop,
	esp_dma_isactive,
	NULL,			/* gl_clear_latched_intr */
};

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
struct ncr53c9x_glue esp_glue1 = {
	esp_rdreg1,
	esp_wrreg1,
	esp_dma_isintr,
	esp_dma_reset,
	esp_dma_intr,
	esp_dma_setup,
	esp_dma_go,
	esp_dma_stop,
	esp_dma_isactive,
	NULL,			/* gl_clear_latched_intr */
};
#endif

int
espmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (ca->ca_bustype == BUS_SBUS) {
		if (!sbus_testdma((struct sbus_softc *)parent, ca))
			return (0);

		if (strcmp("SUNW,fas", ra->ra_name) == 0 ||
		    strcmp("ptscII", ra->ra_name) == 0)
			return (1);
	}
#endif

	if (strcmp(cf->cf_driver->cd_name, ra->ra_name))
		return (0);

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (ca->ca_bustype == BUS_SBUS)
		return (1);
#endif
#ifdef SUN4
	if (cpuinfo.cpu_type == CPUTYP_4_100)
		return (0);
#endif
	ra->ra_len = NBPG;
	return (probeget(ra->ra_vaddr, 1) != -1);
}

/*
 * Attach this instance, and then all the sub-devices
 */
void
espattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	register struct confargs *ca = aux;
	struct esp_softc *esc = (void *)self;
	struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
	struct bootpath *bp;
	int dmachild;
	unsigned int uid = 0;

	/*
	 * If no interrupts properties, bail out: this might happen
	 * on the Sparc X terminal.
	 */
	if (ca->ca_ra.ra_nintr != 1) {
		printf(": expected 1 interrupt, got %d\n", ca->ca_ra.ra_nintr);
		return;
	}

	esc->sc_pri = ca->ca_ra.ra_intr[0].int_pri;
	printf(" pri %d", esc->sc_pri);

	/* Other settings */
	esc->sc_node = ca->ca_ra.ra_node;
	if (ca->ca_bustype == BUS_SBUS) {
		sc->sc_id = getpropint(esc->sc_node, "initiator-id", 7);
		sc->sc_freq = getpropint(esc->sc_node, "clock-frequency", -1);
	} else {
		sc->sc_id = 7;
		sc->sc_freq = 24000000;
	}
	if (sc->sc_freq < 0)
		sc->sc_freq = ((struct sbus_softc *)
		    sc->sc_dev.dv_parent)->sc_clockfreq;

	/* gimme MHz */
	sc->sc_freq /= 1000000;

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (ca->ca_bustype == BUS_SBUS &&
	    strcmp("SUNW,fas", ca->ca_ra.ra_name) == 0) {
		struct dma_softc *dsc;

		/*
		 * fas has 2 register spaces: DMA (lsi64854) and SCSI core
		 * (ncr53c9x).
		 */
		if (ca->ca_ra.ra_nreg != 2) {
			printf(": expected 2 register spaces, found %d\n",
			    ca->ca_ra.ra_nreg);
			return;
		}

		/*
		 * Allocate a softc for the DMA companion, which will not
		 * get a regular attachment.
		 */
		dsc = malloc(sizeof(*dsc), M_DEVBUF, M_NOWAIT | M_ZERO);
		if (dsc == NULL) {
			printf(": could not allocate dma softc\n");
			return;
		}
		strlcpy(dsc->sc_dev.dv_xname, sc->sc_dev.dv_xname,
		    sizeof(dsc->sc_dev.dv_xname));
		esc->sc_dma = dsc;

		/*
		 * Map DMA registers
		 */
		dsc->sc_regs = (struct dma_regs *)mapiodev(&ca->ca_ra.ra_reg[0],
		    0, ca->ca_ra.ra_reg[0].rr_len);
		if (dsc->sc_regs == NULL) {
			printf(": can't map DMA registers\n");
			return;
		}
		dsc->sc_rev = dsc->sc_regs->csr & D_DEV_ID;
		dsc->sc_esp = esc;

#ifdef SUN4M
		/*
		 * Get transfer burst size from PROM and plug it into the
		 * controller registers. This is needed on the Sun4m; do
		 * others need it too?
		 */
		if (CPU_ISSUN4M) {
			int sbusburst;

			sbusburst = ((struct sbus_softc *)parent)->sc_burst;
			if (sbusburst == 0)
				sbusburst = SBUS_BURST_32 - 1;	/* 1 -> 16 */

			dsc->sc_burst =
			    getpropint(ca->ca_ra.ra_node, "burst-sizes", -1);
			if (dsc->sc_burst == -1)
				/* take SBus burst sizes */
				dsc->sc_burst = sbusburst;

			/* Clamp at parent's burst sizes */
			dsc->sc_burst &= sbusburst;
		}
#endif	/* SUN4M */

		/* indirect functions */
		dma_setuphandlers(dsc);

		/*
		 * Map SCSI core registers
		 */
		esc->sc_reg = (volatile u_char *)mapiodev(&ca->ca_ra.ra_reg[1],
		    0, ca->ca_ra.ra_reg[1].rr_len);
		if (esc->sc_reg == NULL) {
			printf(": can't map SCSI core registers\n");
			return;
		}

		dmachild = 0;
	} else
#endif
	{
		/*
		 * Map my registers in, if they aren't already in virtual
		 * address space.
		 */
		if (ca->ca_ra.ra_vaddr)
			esc->sc_reg = (volatile u_char *) ca->ca_ra.ra_vaddr;
		else {
			esc->sc_reg = (volatile u_char *)
			    mapiodev(ca->ca_ra.ra_reg, 0, ca->ca_ra.ra_len);
		}

		dmachild = strcmp(parent->dv_cfdata->cf_driver->cd_name, "dma") == 0;
		if (dmachild) {
			esc->sc_dma = (struct dma_softc *)parent;
			esc->sc_dma->sc_esp = esc;
		} else {
			/*
			 * Find the DMA by poking around the DMA device
			 * structures.
			 *
			 * What happens here is that if the DMA driver has
			 * not been configured, then this returns a NULL
			 * pointer. Then when the DMA actually gets configured,
			 * it does the opposite test, and if the sc->sc_esp
			 * field in its softc is NULL, then tries to find the
			 * matching esp driver.
			 */
			esc->sc_dma = (struct dma_softc *)
			    getdevunit("dma", sc->sc_dev.dv_unit);

			/*
			 * ...and a back pointer to us, for DMA.
			 */
			if (esc->sc_dma)
				esc->sc_dma->sc_esp = esc;
			else {
				printf("\n");
				panic("espattach: no dma found");
			}
		}
	}

	/*
	 * Set up glue for MI code.
	 */
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (ca->ca_bustype == BUS_SBUS &&
	    strcmp("ptscII", ca->ca_ra.ra_name) == 0) {
		sc->sc_glue = &esp_glue1;
	} else
#endif
		sc->sc_glue = &esp_glue;

	/*
	 * XXX More of this should be in ncr53c9x_attach(), but
	 * XXX should we really poke around the chip that much in
	 * XXX the MI code?  Think about this more...
	 */

	/*
	 * It is necessary to try to load the 2nd config register here,
	 * to find out what rev the esp chip is, else the ncr53c9x_reset
	 * will not set up the defaults correctly.
	 */
	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
	sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_RPE;
	sc->sc_cfg3 = NCRCFG3_CDB;
	NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);

	if ((NCR_READ_REG(sc, NCR_CFG2) & ~NCRCFG2_RSVD) !=
	    (NCRCFG2_SCSI2 | NCRCFG2_RPE)) {
		sc->sc_rev = NCR_VARIANT_ESP100;
	} else {
		sc->sc_cfg2 = NCRCFG2_SCSI2;
		NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);
		sc->sc_cfg3 = 0;
		NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
		sc->sc_cfg3 = (NCRCFG3_CDB | NCRCFG3_FCLK);
		NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
		if (NCR_READ_REG(sc, NCR_CFG3) !=
		    (NCRCFG3_CDB | NCRCFG3_FCLK)) {
			sc->sc_rev = NCR_VARIANT_ESP100A;
		} else {
			/* NCRCFG2_FE enables > 64K transfers */
			sc->sc_cfg2 |= NCRCFG2_FE;
			sc->sc_cfg3 = 0;
			NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
			sc->sc_rev = NCR_VARIANT_ESP200;

			/* XXX spec says it's valid after power up or chip reset */
			uid = NCR_READ_REG(sc, NCR_UID);
			if (((uid & 0xf8) >> 3) == 0x0a) /* XXX */
				sc->sc_rev = NCR_VARIANT_FAS366;
		}
	}

	/*
	 * XXX minsync and maxxfer _should_ be set up in MI code,
	 * XXX but it appears to have some dependency on what sort
	 * XXX of DMA we're hooked up to, etc.
	 */

	/*
	 * This is the value used to start sync negotiations
	 * Note that the NCR register "SYNCTP" is programmed
	 * in "clocks per byte", and has a minimum value of 4.
	 * The SCSI period used in negotiation is one-fourth
	 * of the time (in nanoseconds) needed to transfer one byte.
	 * Since the chip's clock is given in MHz, we have the following
	 * formula: 4 * period = (1000 / freq) * 4
	 */
	sc->sc_minsync = 1000 / sc->sc_freq;

	/*
	 * Alas, we must now modify the value a bit, because it's
	 * only valid when can switch on FASTCLK and FASTSCSI bits
	 * in config register 3...
	 */
	switch (sc->sc_rev) {
	case NCR_VARIANT_ESP100:
		sc->sc_maxxfer = 64 * 1024;
		sc->sc_minsync = 0;	/* No synch on old chip? */
		break;

	case NCR_VARIANT_ESP100A:
		sc->sc_maxxfer = 64 * 1024;
		/* Min clocks/byte is 5 */
		sc->sc_minsync = ncr53c9x_cpb2stp(sc, 5);
		break;

	case NCR_VARIANT_ESP200:
	case NCR_VARIANT_FAS366:
		sc->sc_maxxfer = 16 * 1024 * 1024;
		/* XXX - do actually set FAST* bits */
		break;
	}

	/* and the interrupts */
	esc->sc_ih.ih_fun = (void *) ncr53c9x_intr;
	esc->sc_ih.ih_arg = sc;
	intr_establish(esc->sc_pri, &esc->sc_ih, IPL_BIO, self->dv_xname);

	/*
	 * If the boot path is "esp" at the moment and it's me, then
	 * walk our pointer to the sub-device, ready for the config
	 * below.
	 */
	bp = ca->ca_ra.ra_bp;
	if (bp != NULL && (strcmp(bp->name, "esp") == 0 ||
	    strcmp(bp->name, ca->ca_ra.ra_name) == 0)) {
		switch (ca->ca_bustype) {
		case BUS_SBUS:
			if (SAME_ESP(sc, bp, ca))
				bootpath_store(1, bp + 1);
			break;
		default:
			if (bp->val[0] == -1 &&
			    bp->val[1] == sc->sc_dev.dv_unit)
				bootpath_store(1, bp + 1);
			break;
		}
	}

	/* Turn on target selection using the `dma' method */
	if (sc->sc_rev != NCR_VARIANT_FAS366)
		sc->sc_features |= NCR_F_DMASELECT;

	/* Do the common parts of attachment. */
	ncr53c9x_attach(sc);

	bootpath_store(1, NULL);
}

/*
 * Glue functions.
 */

u_char
esp_read_reg(sc, reg)
	struct ncr53c9x_softc *sc;
	int reg;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (esc->sc_reg[reg * 4]);
}

void
esp_write_reg(sc, reg, val)
	struct ncr53c9x_softc *sc;
	int reg;
	u_char val;
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	u_char v = val;

	esc->sc_reg[reg * 4] = v;
}

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
u_char
esp_rdreg1(sc, reg)
	struct ncr53c9x_softc *sc;
	int reg;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (esc->sc_reg[reg]);
}

void
esp_wrreg1(sc, reg, val)
	struct ncr53c9x_softc *sc;
	int reg;
	u_char val;
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	u_char v = val;

	esc->sc_reg[reg] = v;
}
#endif

int
esp_dma_isintr(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (DMA_ISINTR(esc->sc_dma));
}

void
esp_dma_reset(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	DMA_RESET(esc->sc_dma);
}

int
esp_dma_intr(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (DMA_INTR(esc->sc_dma));
}

int
esp_dma_setup(sc, addr, len, datain, dmasize)
	struct ncr53c9x_softc *sc;
	caddr_t *addr;
	size_t *len;
	int datain;
	size_t *dmasize;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (DMA_SETUP(esc->sc_dma, addr, len, datain, dmasize));
}

void
esp_dma_go(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	DMA_GO(esc->sc_dma);
}

void
esp_dma_stop(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	DMACSR(esc->sc_dma) &= ~D_EN_DMA;
}

int
esp_dma_isactive(sc)
	struct ncr53c9x_softc *sc;
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return (DMA_ISACTIVE(esc->sc_dma));
}


code:
/*	$OpenBSD: espvar.h,v 1.6 2006/06/02 20:00:54 miod Exp $	*/
/*	$NetBSD: espvar.h,v 1.19 1997/02/27 01:16:21 thorpej Exp $	*/

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

struct esp_softc {
	struct ncr53c9x_softc sc_ncr53c9x;	/* glue to MI code */

	struct intrhand sc_ih;			/* intr handler */

	volatile u_char *sc_reg;		/* the registers */
	struct dma_softc *sc_dma;		/* pointer to my dma */

	/* openprom stuff */
	int sc_node;				/* PROM node ID */
	int sc_pri;				/* SBUS priority */
};

#define SAME_ESP(sc, bp, ca) \
	((bp->val[0] == ca->ca_slot && bp->val[1] == ca->ca_offset) || \
	 (bp->val[0] == -1 && bp->val[1] == sc->sc_dev.dv_unit))


code:
/*	$OpenBSD: fb.c,v 1.55 2015/02/07 23:29:29 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>
#if defined(SUN4)
#include <machine/eeprom.h>
#include <sparc/dev/pfourreg.h>
#endif
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
#include <machine/bsd_openprom.h>
#endif

#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

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
static int a2int(char *, int);
#endif
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 bustype)
{
	int def_linebytes;

	switch (bustype) {
	case BUS_VME16:
	case BUS_VME32:
	case BUS_OBIO:
#if defined(SUN4M)
		/* 4m may have SBus-like framebuffer on obio */
		if (CPU_ISSUN4M) {
			goto obpsize;
		}
#endif
		/* Set up some defaults. */
		sf->sf_width = def_width;
		sf->sf_height = def_height;
		sf->sf_depth = def_depth;

#if defined(SUN4)
		/*
		 * This is not particularly useful on Sun 4 VME framebuffers.
		 * The EEPROM only contains info about the built-in.
		 */
		if (CPU_ISSUN4 && bustype == BUS_OBIO) {
			struct eeprom *eep = (struct eeprom *)eeprom_va;

			if (ISSET(sf->sf_flags, FB_PFOUR)) {
				volatile u_int32_t pfour;
				u_int size;

				pfour = *sf->sf_pfour;

				/*
				 * Use the pfour register to determine
				 * the size.  Note that the cgsix and
				 * cgeight don't use this size encoding.
				 * In this case, we have to settle
				 * for the defaults we were provided
				 * with.
				 */
				if ((PFOUR_ID(pfour) == PFOUR_ID_COLOR24) ||
				    (PFOUR_ID(pfour) == PFOUR_ID_FASTCOLOR))
					size = 0x00; /* invalid */
				else
					size = PFOUR_SIZE(pfour);

				switch (size) {
				case PFOUR_SIZE_1152X900:
					sf->sf_width = 1152;
					sf->sf_height = 900;
					break;
				case PFOUR_SIZE_1024X1024:
					sf->sf_width = 1024;
					sf->sf_height = 1024;
					break;
				case PFOUR_SIZE_1280X1024:
					sf->sf_width = 1280;
					sf->sf_height = 1024;
					break;
				case PFOUR_SIZE_1600X1280:
					sf->sf_width = 1600;
					sf->sf_height = 1280;
					break;
				case PFOUR_SIZE_1440X1440:
					sf->sf_width = 1440;
					sf->sf_height = 1440;
					break;
				case PFOUR_SIZE_640X480:
					sf->sf_width = 640;
					sf->sf_height = 480;
					break;
				}
			} else if (eep != NULL) {
				switch (eep->ee_diag.eed_scrsize) {
				case EED_SCR_1152X900:
					sf->sf_width = 1152;
					sf->sf_height = 900;
					break;
				case EED_SCR_1024X1024:
					sf->sf_width = 1024;
					sf->sf_height = 1024;
					break;
				case EED_SCR_1600X1280:
					sf->sf_width = 1600;
					sf->sf_height = 1280;
					break;
				case EED_SCR_1440X1440:
					sf->sf_width = 1440;
					sf->sf_height = 1440;
					break;
				case EED_SCR_640X480:
					sf->sf_width = 640;
					sf->sf_height = 480;
					break;
				case EED_SCR_1280X1024:
					sf->sf_width = 1280;
					sf->sf_height = 1024;
					break;
				}
			}
		}
#endif /* SUN4 */
#if defined(SUN4M)
		if (CPU_ISSUN4M) {
			/* XXX: need code to find 4/600 vme screen size */
		}
#endif /* SUN4M */

		sf->sf_linebytes = (sf->sf_width * sf->sf_depth) / 8;
		break;

	case BUS_SBUS:
#if defined(SUN4M)
obpsize:
#endif
		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;

		break;
	}

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

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
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);
}
#endif

/* 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 isconsole)
{
	struct rasops_info *ri = &sf->sf_ro;
	int cols, rows;
#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	int fw, fh, wt, wl;
#endif

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

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (!CPU_ISSUN4) {
		rows = a2int(getpropstring(optionsnode, "screen-#rows"), 34);
		cols = a2int(getpropstring(optionsnode, "screen-#columns"), 80);
	}
#endif
#if defined(SUN4)
	if (CPU_ISSUN4) {
		struct eeprom *ep = (struct eeprom *)eeprom_va;

		if (ep != NULL) {
			rows = (u_short)ep->ee_diag.eed_rowsize;
			cols = (u_short)ep->ee_diag.eed_colsize;
			/* deal with broken nvram contents... */
			if (rows <= 0)
				rows = 34;
			if (cols <= 0)
				cols = 80;
		} else {
			rows = 34;
			cols = 80;
		}
	}
#endif

	/*
	 * 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.
	 *
	 * Note that, on sun4 systems, no frame buffer supports display
	 * resolutions requiring a font switch, so it is safe to bypass
	 * this chunk.
	 */

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	if (!CPU_ISSUN4 && 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 {
			/*
			 * We have valid font metrics, but the
			 * window-top and window-left values might not
			 * be available.  Also, we have to make sure
			 * their 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
#endif
		ri->ri_flg |= RI_CENTER;

	rasops_init(ri, rows, cols);

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	/*
	 * 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;
	}
#endif

	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 hilighted
		 * 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 (CPU_ISSUN4 || 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);
}

#if defined(SUN4)

/*
 * Support routines for P4 framebuffers.
 */

/*
 * Probe for a P4 framebuffer.  Return values:
 *	PFOUR_NOTPFOUR		framebuffer is not a P4 framebuffer
 *	otherwise returns P4 ID
 */
int
fb_pfour_id(volatile void *va)
{
	volatile u_int32_t val, save, *pfour = va;

	/* Read the pfour register. */
	save = *pfour;

	/*
	 * Try to modify the type code.  If it changes, put the
	 * original value back, and notify the caller that it's
	 * not a pfour framebuffer.
	 */
	val = save & ~PFOUR_REG_RESET;
	*pfour = (val ^ PFOUR_FBTYPE_MASK);
	if ((*pfour ^ val) & PFOUR_FBTYPE_MASK) {
		*pfour = save;
		return (PFOUR_NOTPFOUR);
	}

	return (PFOUR_ID(val));
}

/*
 * Screen burner routine for P4
 */
void
fb_pfour_burner(void *v, u_int enable, u_int flags)
{
	struct sunfb *sf = (struct sunfb *)v;
	volatile u_int32_t pfour;

	pfour = *sf->sf_pfour & ~(PFOUR_REG_INTCLR | PFOUR_REG_VIDEO);
	*sf->sf_pfour = pfour | (enable ? PFOUR_REG_VIDEO : 0);
}

#endif /* SUN4 */

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
int
fb_get_console_metrics(int *fontwidth, int *fontheight, int *wtop, int *wleft)
{
	int *romwidth, *romheight, *windowtop, *windowleft;
	char buf[200];

	/*
	 * Get the PROM font metrics.
	 */

	/*
	 * char-height and char-width are global in older proms (rom vector
	 * < 2) and in some newer proms.  They are local in version 2.9.  The
	 * correct cutoff point is unknown, as yet; we use 2.9 here.
	 */
	if (promvec->pv_romvec_vers < 2 || promvec->pv_printrev < 0x00020009) {
		if (snprintf(buf, sizeof buf,
		    "' char-height >body >user %lx ! "
		    "' char-width >body >user %lx !",
		    (vaddr_t)&romheight, (vaddr_t)&romwidth) >= sizeof buf)
			return (1);
	} else {
		if (snprintf(buf, sizeof buf, "stdout @ is my-self "
		    "addr char-height %lx ! addr char-width %lx !",
		    (vaddr_t)&romheight, (vaddr_t)&romwidth) >= sizeof buf)
			return (1);
	}

	romheight = romwidth = NULL;
	rominterpret(buf);

	if (romheight == NULL || romwidth == NULL ||
	    *romheight == 0 || *romwidth == 0)
		return (1);

	*fontwidth = *romwidth;
	*fontheight = *romheight;

	/*
	 * Get the PROM console window position, if possible.
	 */

	*wtop = *wleft = 0;	/* if no values available... */
	if (promvec->pv_romvec_vers < 2 || promvec->pv_printrev < 0x00020009)
		return (0);	/* not available */

	if (snprintf(buf, sizeof buf, "stdout @ is my-self "
	    "addr window-top %lx ! addr window-left %lx !",
	    (vaddr_t)&windowtop, (vaddr_t)&windowleft) >= sizeof buf)
		return (0);	/* XXX shouldn't happen */

	windowtop = windowleft = NULL;
	rominterpret(buf);

	if (windowtop != NULL)
		*wtop = *windowtop;
	if (windowleft != NULL)
		*wleft = *windowleft;

	return (0);
}
#endif	/* SUN4C || SUN4D || SUN4E || SUN4M */

#endif	/* NWSDISPLAY */