code:
/*	$OpenBSD: p9000.c,v 1.25 2015/03/28 19:07:07 miod Exp $	*/

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

/*
 * Driver for the Tadpole SPARCbook 3 on-board display.
 * Heavily based on the p9100 driver.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/tty.h>
#include <sys/conf.h>

#include <uvm/uvm_extern.h>

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

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

#include <sparc/dev/btreg.h>
#include <sparc/dev/btvar.h>
#include <sparc/dev/bt445reg.h>
#include <sparc/dev/bt445var.h>

#include <dev/ic/p9000.h>

#include "tctrl.h"
#if NTCTRL > 0
#include <sparc/dev/tctrlvar.h>
#endif

/* per-display variables */
struct p9000_softc {
	struct	sunfb sc_sunfb;		/* common base part */
	struct	rom_reg	sc_phys;	/* phys address description */
	volatile u_int8_t *sc_cmd;	/* command registers (dac, etc) */
	volatile u_int8_t *sc_ctl;	/* control registers (draw engine) */
	union	bt_cmap sc_cmap;	/* Brooktree color map */
	volatile u_int8_t *sc_ramdac;	/* BT445 registers */
	struct	intrhand sc_ih;
	u_int32_t	sc_junk;	/* throwaway value */
};

int	p9000_ioctl(void *, u_long, caddr_t, int, struct proc *);
static __inline__
void	p9000_loadcmap_deferred(struct p9000_softc *, u_int, u_int);
void	p9000_loadcmap_immediate(struct p9000_softc *, u_int, u_int);
paddr_t	p9000_mmap(void *, off_t, int);
void	p9000_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
void	p9000_burner(void *, u_int, u_int);
int	p9000_intr(void *);

struct wsdisplay_accessops p9000_accessops = {
	.ioctl = p9000_ioctl,
	.mmap = p9000_mmap,
	.burn_screen = p9000_burner
};

int	p9000_ras_copycols(void *, int, int, int, int);
int	p9000_ras_copyrows(void *, int, int, int);
int	p9000_ras_do_cursor(struct rasops_info *);
int	p9000_ras_erasecols(void *, int, int, int, long int);
int	p9000_ras_eraserows(void *, int, int, long int);
void	p9000_ras_init(struct p9000_softc *);

int	p9000match(struct device *, void *, void *);
void	p9000attach(struct device *, struct device *, void *);

struct cfattach pninek_ca = {
	sizeof (struct p9000_softc), p9000match, p9000attach
};

struct cfdriver pninek_cd = {
	NULL, "pninek", DV_DULL
};

/*
 * SBus registers mappings
 */
#define	P9000_NREG	5	/* actually, 7 total */
#define	P9000_REG_CTL	0
#define	P9000_REG_CMD	1
#define	P9000_REG_VRAM	4

/*
 * P9000 read/write macros
 */

#define	P9000_READ_CTL(sc,reg) \
	*(volatile u_int32_t *)((sc)->sc_ctl + (reg))
#define	P9000_READ_CMD(sc,reg) \
	*(volatile u_int32_t *)((sc)->sc_cmd + (reg))

#define	P9000_WRITE_CTL(sc,reg,value) \
	*(volatile u_int32_t *)((sc)->sc_ctl + (reg)) = (value)
#define	P9000_WRITE_CMD(sc,reg,value) \
	*(volatile u_int32_t *)((sc)->sc_cmd + (reg)) = (value)

/*
 * On the Tadpole, the first write to a register group is ignored until
 * the proper group address is latched, which can be done by reading from the
 * register group first.
 *
 * Register groups are 0x80 bytes long (i.e. it is necessary to force a read
 * when writing to an address which upper 25 bit differ from the previous
 * read or write operation).
 *
 * This is specific to the Tadpole design, and not a limitation of the
 * Power 9000 hardware.
 */
#define	P9000_SELECT_SCR(sc) \
	(sc)->sc_junk = P9000_READ_CTL(sc, P9000_SYSTEM_CONFIG)
#define	P9000_SELECT_VCR(sc) \
	(sc)->sc_junk = P9000_READ_CTL(sc, P9000_HCR)
#define	P9000_SELECT_VRAM(sc) \
	(sc)->sc_junk = P9000_READ_CTL(sc, P9000_MCR)
#define	P9000_SELECT_PE(sc) \
	(sc)->sc_junk = P9000_READ_CMD(sc, P9000_PE_STATUS)
#define	P9000_SELECT_DE_LOW(sc)	\
	(sc)->sc_junk = P9000_READ_CMD(sc, P9000_DE_FG_COLOR)
#define	P9000_SELECT_DE_HIGH(sc) \
	(sc)->sc_junk = P9000_READ_CMD(sc, P9000_DE_PATTERN(0))
#define	P9000_SELECT_COORD(sc,field) \
	(sc)->sc_junk = P9000_READ_CMD(sc, field)

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

	if (strcmp("p9000", ra->ra_name))
		return (0);

	/*
	 * If this is not the console device, chances are the
	 * frame buffer is not completely initialized, and access
	 * to some of its control registers could hang (this is
	 * the case on p9100). Until this can be verified, do
	 * not attach if console is on serial.
	 */
	if (ra->ra_node != fbnode)
		return (0);

	return (1);
}

void
p9000attach(struct device *parent, struct device *self, void *args)
{
	struct p9000_softc *sc = (struct p9000_softc *)self;
	struct confargs *ca = args;
	int node, pri, isconsole, scr;
	struct device *btdev;
	extern struct cfdriver btcham_cd;

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

#ifdef DIAGNOSTIC
	if (ca->ca_ra.ra_nreg < P9000_NREG) {
		printf(": expected %d registers, got only %d\n",
		    P9000_NREG, ca->ca_ra.ra_nreg);
		return;
	}
#endif

	/*
	 * Find the RAMDAC device. It should have attached before, since it
	 * attaches at obio. If, for some reason, it did not, it's not worth
	 * going any further.
	 *
	 * We rely upon the PROM to properly initialize the RAMDAC in a safe
	 * mode.
	 */
	btdev = btcham_cd.cd_ndevs != 0 ? btcham_cd.cd_devs[0] : NULL;
	if (btdev != NULL)
		sc->sc_ramdac = ((struct bt445_softc *)btdev)->sc_regs;

	if (sc->sc_ramdac == NULL) {
		printf(": bt445 did not attach previously\n");
		return;
	}

	sc->sc_phys = ca->ca_ra.ra_reg[P9000_REG_VRAM];

	sc->sc_ctl = mapiodev(&(ca->ca_ra.ra_reg[P9000_REG_CTL]), 0,
	    ca->ca_ra.ra_reg[0].rr_len);
	sc->sc_cmd = mapiodev(&(ca->ca_ra.ra_reg[P9000_REG_CMD]), 0,
	    ca->ca_ra.ra_reg[1].rr_len);

	node = ca->ca_ra.ra_node;
	isconsole = node == fbnode;

	fb_setsize(&sc->sc_sunfb, 8, 640, 480, node, ca->ca_bustype);
	sc->sc_sunfb.sf_ro.ri_bits = mapiodev(&sc->sc_phys, 0,
	    round_page(sc->sc_sunfb.sf_fbsize));
	sc->sc_sunfb.sf_ro.ri_hw = sc;

	P9000_SELECT_SCR(sc);
	scr = P9000_READ_CTL(sc, P9000_SYSTEM_CONFIG);

	printf(": rev %x, %dx%d\n", scr & SCR_ID_MASK,
	    sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);

	/* Disable frame buffer interrupts */
	P9000_SELECT_SCR(sc);
	P9000_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE, IER_MASTER_ENABLE | 0);

	sc->sc_ih.ih_fun = p9000_intr;
	sc->sc_ih.ih_arg = sc;
	intr_establish(pri, &sc->sc_ih, IPL_FB, self->dv_xname);

	fbwscons_init(&sc->sc_sunfb, isconsole);
	fbwscons_setcolormap(&sc->sc_sunfb, p9000_setcolor);

	/*
	 * Plug-in accelerated console operations.
	 */
	if (sc->sc_sunfb.sf_dev.dv_cfdata->cf_flags != 0)
		p9000_ras_init(sc);

	/* enable video */
	p9000_burner(sc, 1, 0);

	if (isconsole)
		fbwscons_console_init(&sc->sc_sunfb, -1);

	fbwscons_attach(&sc->sc_sunfb, &p9000_accessops, isconsole);
}

int
p9000_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct p9000_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_cmap *cm;
#if NTCTRL > 0
	struct wsdisplay_param *dp;
#endif
	int error;

	switch (cmd) {

	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_SB_P9000;
		break;

	case WSDISPLAYIO_SMODE:
		/* Restore proper acceleration state upon leaving X11 */
		if (*(u_int *)data == WSDISPLAYIO_MODE_EMUL) {
			if (sc->sc_sunfb.sf_dev.dv_cfdata->cf_flags != 0)
				p9000_ras_init(sc);
		}
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width  = sc->sc_sunfb.sf_width;
		wdf->depth  = sc->sc_sunfb.sf_depth;
		wdf->cmsize = 256;
		break;

	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = bt_getcmap(&sc->sc_cmap, cm);
		if (error)
			return (error);
		break;

	case WSDISPLAYIO_PUTCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = bt_putcmap(&sc->sc_cmap, cm);
		if (error)
			return (error);
		p9000_loadcmap_deferred(sc, cm->index, cm->count);
		break;

#if NTCTRL > 0
	case WSDISPLAYIO_GETPARAM:
		dp = (struct wsdisplay_param *)data;

		switch (dp->param) {
		case WSDISPLAYIO_PARAM_BRIGHTNESS:
			dp->min = 0;
			dp->max = 255;
			dp->curval = tadpole_get_brightness();
			break;
		case WSDISPLAYIO_PARAM_BACKLIGHT:
			dp->min = 0;
			dp->max = 1;
			dp->curval = tadpole_get_video() & TV_ON ? 1 : 0;
			break;
		default:
			return (-1);
		}
		break;

	case WSDISPLAYIO_SETPARAM:
		dp = (struct wsdisplay_param *)data;

		switch (dp->param) {
		case WSDISPLAYIO_PARAM_BRIGHTNESS:
			tadpole_set_brightness(dp->curval);
			break;
		case WSDISPLAYIO_PARAM_BACKLIGHT:
			tadpole_set_video(dp->curval);
			break;
		default:
			return (-1);
		}
		break;
#endif	/* NTCTRL > 0 */

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return (-1);	/* not supported yet */
	}

	return (0);
}

paddr_t
p9000_mmap(void *v, off_t offset, int prot)
{
	struct p9000_softc *sc = v;

	if (offset & PGOFSET)
		return (-1);

	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) {
		return (REG2PHYS(&sc->sc_phys, offset) | PMAP_NC);
	}

	return (-1);
}

void
p9000_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct p9000_softc *sc = v;
	union bt_cmap *bcm = &sc->sc_cmap;

	bcm->cm_map[index][0] = r;
	bcm->cm_map[index][1] = g;
	bcm->cm_map[index][2] = b;
	p9000_loadcmap_immediate(sc, index, 1);
}

void
p9000_loadcmap_immediate(struct p9000_softc *sc, u_int start, u_int ncolors)
{
	sc->sc_ramdac[BT445_ADDRESS] = start;
	for (ncolors += start; start < ncolors; start++) {
		sc->sc_ramdac[BT445_PALDATA] = sc->sc_cmap.cm_map[start][0];
		sc->sc_ramdac[BT445_PALDATA] = sc->sc_cmap.cm_map[start][1];
		sc->sc_ramdac[BT445_PALDATA] = sc->sc_cmap.cm_map[start][2];
	}
}

static __inline__ void
p9000_loadcmap_deferred(struct p9000_softc *sc, u_int start, u_int ncolors)
{
	/* Schedule an interrupt for next retrace */
	P9000_SELECT_SCR(sc);
	P9000_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE,
	    IER_MASTER_ENABLE | IER_MASTER_INTERRUPT |
	    IER_VBLANK_ENABLE | IER_VBLANK_INTERRUPT);
}

void
p9000_burner(void *v, u_int on, u_int flags)
{
	struct p9000_softc *sc = v;
	u_int32_t vcr;
	int s;

	s = splhigh();
	P9000_SELECT_VCR(sc);
	vcr = P9000_READ_CTL(sc, P9000_SRTC1);
	if (on)
		vcr |= SRTC1_VIDEN;
	else
		vcr &= ~SRTC1_VIDEN;
	P9000_WRITE_CTL(sc, P9000_SRTC1, vcr);
#if NTCTRL > 0
	tadpole_set_video(on);
#endif
	splx(s);
}

int
p9000_intr(void *v)
{
	struct p9000_softc *sc = v;

	if (P9000_READ_CTL(sc, P9000_INTERRUPT) & IER_VBLANK_INTERRUPT) {
		p9000_loadcmap_immediate(sc, 0, 256);

		/* Disable further interrupts now */
		/* P9000_SELECT_SCR(sc); */
		P9000_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE,
		    IER_MASTER_ENABLE | 0);

		/* Clear interrupt condition */
		P9000_WRITE_CTL(sc, P9000_INTERRUPT,
		    IER_VBLANK_ENABLE | 0);

		return (1);
	}

	return (0);
}

/*
 * Accelerated text console code
 */

static int p9000_drain(struct p9000_softc *);

static int
p9000_drain(struct p9000_softc *sc)
{
	u_int i;

	for (i = 10000; i != 0; i--) {
		if ((P9000_READ_CMD(sc, P9000_PE_STATUS) &
		    (STATUS_QUAD_BUSY | STATUS_BLIT_BUSY)) == 0)
			break;
	}

	return (i);
}

void
p9000_ras_init(struct p9000_softc *sc)
{

	if (p9000_drain(sc) == 0)
		return;

	sc->sc_sunfb.sf_ro.ri_ops.copycols = p9000_ras_copycols;
	sc->sc_sunfb.sf_ro.ri_ops.copyrows = p9000_ras_copyrows;
	sc->sc_sunfb.sf_ro.ri_ops.erasecols = p9000_ras_erasecols;
	sc->sc_sunfb.sf_ro.ri_ops.eraserows = p9000_ras_eraserows;
	sc->sc_sunfb.sf_ro.ri_do_cursor = p9000_ras_do_cursor;

	/*
	 * Setup safe defaults for the parameter and drawing engines, in
	 * order to minimize the operations to do for ri_ops.
	 */

	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_DRAWMODE,
	    DM_PICK_CONTROL | 0 | DM_BUFFER_CONTROL | DM_BUFFER_ENABLE0);

	P9000_WRITE_CMD(sc, P9000_DE_PATTERN_ORIGIN_X, 0);
	P9000_WRITE_CMD(sc, P9000_DE_PATTERN_ORIGIN_Y, 0);
	/* enable all planes */
	P9000_WRITE_CMD(sc, P9000_DE_PLANEMASK, 0xff);

	/* Unclip */
	P9000_WRITE_CMD(sc, P9000_DE_WINMIN, 0);
	P9000_WRITE_CMD(sc, P9000_DE_WINMAX,
	    P9000_COORDS(sc->sc_sunfb.sf_width - 1, sc->sc_sunfb.sf_height - 1));

	P9000_SELECT_PE(sc);
	P9000_WRITE_CMD(sc, P9000_PE_WINOFFSET, 0);
	P9000_WRITE_CMD(sc, P9000_PE_INDEX, 0);
	P9000_WRITE_CMD(sc, P9000_PE_WINMIN, 0);
	P9000_WRITE_CMD(sc, P9000_PE_WINMAX,
	    P9000_COORDS(sc->sc_sunfb.sf_width - 1, sc->sc_sunfb.sf_height - 1));
}

int
p9000_ras_copycols(void *v, int row, int src, int dst, int n)
{
	struct rasops_info *ri = v;
	struct p9000_softc *sc = ri->ri_hw;

	n *= ri->ri_font->fontwidth;
	n--;
	src *= ri->ri_font->fontwidth;
	src += ri->ri_xorigin;
	dst *= ri->ri_font->fontwidth;
	dst += ri->ri_xorigin;
	row *= ri->ri_font->fontheight;
	row += ri->ri_yorigin;

	p9000_drain(sc);
	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9000_RASTER_SRC & P9000_RASTER_MASK);

	P9000_SELECT_COORD(sc, P9000_DC_COORD(0));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(0) + P9000_COORD_XY,
	    P9000_COORDS(src, row));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(1) + P9000_COORD_XY,
	    P9000_COORDS(src + n, row + ri->ri_font->fontheight - 1));
	P9000_SELECT_COORD(sc, P9000_DC_COORD(2));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(2) + P9000_COORD_XY,
	    P9000_COORDS(dst, row));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(3) + P9000_COORD_XY,
	    P9000_COORDS(dst + n, row + ri->ri_font->fontheight - 1));

	sc->sc_junk = P9000_READ_CMD(sc, P9000_PE_BLIT);

	p9000_drain(sc);

	return 0;
}

int
p9000_ras_copyrows(void *v, int src, int dst, int n)
{
	struct rasops_info *ri = v;
	struct p9000_softc *sc = ri->ri_hw;

	n *= ri->ri_font->fontheight;
	n--;
	src *= ri->ri_font->fontheight;
	src += ri->ri_yorigin;
	dst *= ri->ri_font->fontheight;
	dst += ri->ri_yorigin;

	p9000_drain(sc);
	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9000_RASTER_SRC & P9000_RASTER_MASK);

	P9000_SELECT_COORD(sc, P9000_DC_COORD(0));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(0) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin, src));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(1) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth - 1, src + n));
	P9000_SELECT_COORD(sc, P9000_DC_COORD(2));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(2) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin, dst));
	P9000_WRITE_CMD(sc, P9000_DC_COORD(3) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth - 1, dst + n));

	sc->sc_junk = P9000_READ_CMD(sc, P9000_PE_BLIT);

	p9000_drain(sc);

	return 0;
}

int
p9000_ras_erasecols(void *v, int row, int col, int n, long int attr)
{
	struct rasops_info *ri = v;
	struct p9000_softc *sc = ri->ri_hw;
	int fg, bg;

	ri->ri_ops.unpack_attr(v, attr, &fg, &bg, NULL);

	n *= ri->ri_font->fontwidth;
	col *= ri->ri_font->fontwidth;
	col += ri->ri_xorigin;
	row *= ri->ri_font->fontheight;
	row += ri->ri_yorigin;

	p9000_drain(sc);
	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9000_RASTER_PATTERN & P9000_RASTER_MASK);
	P9000_WRITE_CMD(sc, P9000_DE_FG_COLOR, bg);

	P9000_SELECT_COORD(sc, P9000_LC_RECT);
	P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col, row));
	P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col + n, row + ri->ri_font->fontheight));

	sc->sc_junk = P9000_READ_CMD(sc, P9000_PE_QUAD);

	p9000_drain(sc);

	return 0;
}

int
p9000_ras_eraserows(void *v, int row, int n, long int attr)
{
	struct rasops_info *ri = v;
	struct p9000_softc *sc = ri->ri_hw;
	int fg, bg;

	ri->ri_ops.unpack_attr(v, attr, &fg, &bg, NULL);

	p9000_drain(sc);
	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9000_RASTER_PATTERN & P9000_RASTER_MASK);
	P9000_WRITE_CMD(sc, P9000_DE_FG_COLOR, bg);

	P9000_SELECT_COORD(sc, P9000_LC_RECT);
	if (n == ri->ri_rows && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
		P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(0, 0));
		P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_width, ri->ri_height));
	} else {
		n *= ri->ri_font->fontheight;
		row *= ri->ri_font->fontheight;
		row += ri->ri_yorigin;

		P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_xorigin, row));
		P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth, row + n));
	}

	sc->sc_junk = P9000_READ_CMD(sc, P9000_PE_QUAD);

	p9000_drain(sc);

	return 0;
}

int
p9000_ras_do_cursor(struct rasops_info *ri)
{
	struct p9000_softc *sc = ri->ri_hw;
	int row, col;

	row = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
	col = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;

	p9000_drain(sc);

	P9000_SELECT_DE_LOW(sc);
	P9000_WRITE_CMD(sc, P9000_DE_RASTER,
	    (P9000_RASTER_PATTERN ^ P9000_RASTER_DST) & P9000_RASTER_MASK);
	P9000_WRITE_CMD(sc, P9000_DE_FG_COLOR, ri->ri_devcmap[WSCOL_BLACK]);

	P9000_SELECT_COORD(sc, P9000_LC_RECT);
	P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col, row));
	P9000_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col + ri->ri_font->fontwidth,
	        row + ri->ri_font->fontheight));

	sc->sc_junk = P9000_READ_CMD(sc, P9000_PE_QUAD);

	p9000_drain(sc);

	return 0;
}


code:
/*	$OpenBSD: p9100.c,v 1.53 2015/03/28 19:07:07 miod Exp $	*/

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

/*
 * color display (p9100) driver.
 * Initially based on cgthree.c and the NetBSD p9100 driver, then hacked
 * beyond recognition.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/tty.h>
#include <sys/conf.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/bsd_openprom.h>
#include <machine/pmap.h>
#include <machine/cpu.h>
#include <machine/conf.h>

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

#include <sparc/dev/btreg.h>
#include <sparc/dev/btvar.h>

#include <dev/ic/ibm525reg.h>
#include <dev/ic/p9000.h>

#include "tctrl.h"
#if NTCTRL > 0
#include <sparc/dev/tctrlvar.h>
#endif

#undef	FIDDLE_WITH_PCI_REGISTERS

/*
 * SBus registers mappings
 */
#define	P9100_NREG		4
#define	P9100_REG_CTL		0
#define	P9100_REG_CMD		1
#define	P9100_REG_VRAM		2
#define	P9100_REG_CONFIG	3

#ifdef	FIDDLE_WITH_PCI_REGISTERS
/*
 * This structure, mapped at register address 0x9100, allows non-PCI
 * designs (such as the SPARCbook) to access the PCI configuration space.
 */
struct p9100_pci {
	volatile u_int32_t	address;	/* within configuration space */
	volatile u_int32_t	data;		/* _byte_ to read or write */
};
#endif

/* per-display variables */
struct p9100_softc {
	struct sunfb	sc_sunfb;	/* common base part */
	struct rom_reg	sc_phys[P9100_NREG - 1];
	volatile u_int8_t *sc_cmd;	/* command registers (dac, etc) */
	volatile u_int8_t *sc_ctl;	/* control registers (draw engine) */
#ifdef	FIDDLE_WITH_PCI_REGISTERS
	struct p9100_pci *sc_pci;	/* pci configuration space access */
#endif
	vsize_t		sc_vramsize;	/* total VRAM available */
	union bt_cmap	sc_cmap;	/* Brooktree color map */
	struct intrhand	sc_ih;
	int		sc_mapmode;
	u_int		sc_flags;
#define	SCF_INTERNAL		0x01	/* internal video enabled */
#define	SCF_EXTERNAL		0x02	/* external video enabled */
#if NTCTRL > 0
#define	SCF_MAPPEDSWITCH	0x04	/* switch mode when leaving emul */
	u_int		sc_mapwidth;	/* non-emul video mode parameters */
	u_int		sc_mapheight;
	u_int		sc_mapdepth;
#endif
	u_int		sc_lcdheight;	/* LCD panel geometry */
	u_int		sc_lcdwidth;

	u_int32_t	sc_junk;	/* throwaway value */
};

void	p9100_burner(void *, u_int, u_int);
void	p9100_external_video(void *, int);
void	p9100_initialize_ramdac(struct p9100_softc *, u_int, u_int);
int	p9100_intr(void *);
int	p9100_ioctl(void *, u_long, caddr_t, int, struct proc *);
static __inline__
void	p9100_loadcmap_deferred(struct p9100_softc *, u_int, u_int);
void	p9100_loadcmap_immediate(struct p9100_softc *, u_int, u_int);
paddr_t	p9100_mmap(void *, off_t, int);
void	p9100_pick_romfont(struct p9100_softc *);
void	p9100_prom(void *);
void	p9100_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
u_int	p9100_read_ramdac(struct p9100_softc *, u_int);
void	p9100_write_ramdac(struct p9100_softc *, u_int, u_int);

struct wsdisplay_accessops p9100_accessops = {
	.ioctl = p9100_ioctl,
	.mmap = p9100_mmap,
	.burn_screen = p9100_burner
};

int	p9100_ras_copycols(void *, int, int, int, int);
int	p9100_ras_copyrows(void *, int, int, int);
int	p9100_ras_do_cursor(struct rasops_info *);
int	p9100_ras_erasecols(void *, int, int, int, long int);
int	p9100_ras_eraserows(void *, int, int, long int);
void	p9100_ras_init(struct p9100_softc *);

int	p9100match(struct device *, void *, void *);
void	p9100attach(struct device *, struct device *, void *);
int	p9100activate(struct device *, int);

struct cfattach pnozz_ca = {
	sizeof (struct p9100_softc), p9100match, p9100attach,
	NULL, p9100activate
};

struct cfdriver pnozz_cd = {
	NULL, "pnozz", DV_DULL
};

/*
 * IBM RGB525 RAMDAC registers
 */

#define	IBM525_WRADDR			0	/* Palette write address */
#define	IBM525_DATA			1	/* Palette data */
#define	IBM525_PIXMASK			2	/* Pixel mask */
#define	IBM525_RDADDR			3	/* Read palette address */
#define	IBM525_IDXLOW			4	/* Register index low */
#define	IBM525_IDXHIGH			5	/* Register index high */
#define	IBM525_REGDATA			6	/* Register data */
#define	IBM525_IDXCONTROL		7	/* Index control */

/*
 * P9100 read/write macros
 */

#define	P9100_READ_CTL(sc,reg) \
	*(volatile u_int32_t *)((sc)->sc_ctl + (reg))
#define	P9100_READ_CMD(sc,reg) \
	*(volatile u_int32_t *)((sc)->sc_cmd + (reg))
#define	P9100_READ_RAMDAC(sc,reg) \
	(*(volatile u_int32_t *)((sc)->sc_ctl + P9100_RAMDAC_REGISTER(reg)) \
	    >> 16)

#define	P9100_WRITE_CTL(sc,reg,value) \
	*(volatile u_int32_t *)((sc)->sc_ctl + (reg)) = (value)
#define	P9100_WRITE_CMD(sc,reg,value) \
	*(volatile u_int32_t *)((sc)->sc_cmd + (reg)) = (value)
#define	P9100_WRITE_RAMDAC(sc,reg,value) \
	*(volatile u_int32_t *)((sc)->sc_ctl + P9100_RAMDAC_REGISTER(reg)) = \
	    ((value) << 16)

/*
 * On the Tadpole, the first write to a register group is ignored until
 * the proper group address is latched, which can be done by reading from the
 * register group first.
 *
 * Register groups are 0x80 bytes long (i.e. it is necessary to force a read
 * when writing to an address which upper 25 bit differ from the previous
 * read or write operation).
 *
 * This is specific to the Tadpole design, and not a limitation of the
 * Power 9100 hardware.
 */
#define	P9100_SELECT_SCR(sc) \
	(sc)->sc_junk = P9100_READ_CTL(sc, P9000_SYSTEM_CONFIG)
#define	P9100_SELECT_VCR(sc) \
	(sc)->sc_junk = P9100_READ_CTL(sc, P9000_HCR)
#define	P9100_SELECT_VRAM(sc) \
	(sc)->sc_junk = P9100_READ_CTL(sc, P9000_MCR)
#define	P9100_SELECT_DAC(sc) \
	(sc)->sc_junk = P9100_READ_CTL(sc, P9100_RAMDAC_REGISTER(0))
#define	P9100_SELECT_PE(sc) \
	(sc)->sc_junk = P9100_READ_CMD(sc, P9000_PE_STATUS)
#define	P9100_SELECT_DE_LOW(sc)	\
	(sc)->sc_junk = P9100_READ_CMD(sc, P9000_DE_FG_COLOR)
#define	P9100_SELECT_DE_HIGH(sc) \
	(sc)->sc_junk = P9100_READ_CMD(sc, P9000_DE_PATTERN(0))
#define	P9100_SELECT_COORD(sc,field) \
	(sc)->sc_junk = P9100_READ_CMD(sc, field)

/*
 * For some reason, every write to a DAC register needs to be followed by a
 * read from the ``free fifo number'' register, supposedly to have the write
 * take effect faster...
 */
#define	P9100_FLUSH_DAC(sc) \
	do { \
		P9100_SELECT_VRAM(sc); \
		(sc)->sc_junk = P9100_READ_CTL(sc, P9100_FREE_FIFO); \
	} while (0)

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

	if (strcmp("p9100", ra->ra_name))
		return (0);

#if NTCTRL == 0
	/*
	 * If this is not the console device, the frame buffer is
	 * not completely initialized, and access to some of its
	 * control registers will hang. We'll need to reprogram
	 * the RAMDAC, and currently this requires assistance
	 * from the tctrl code. Do not attach if it is not available
	 * and console is on serial.
	 */
	if (ra->ra_node != fbnode)
		return (0);
#endif

	return (1);
}

/*
 * Attach a display.
 */
void
p9100attach(struct device *parent, struct device *self, void *args)
{
	struct p9100_softc *sc = (struct p9100_softc *)self;
	struct rasops_info *ri = &sc->sc_sunfb.sf_ro;
	struct confargs *ca = args;
	struct romaux *ra = &ca->ca_ra;
	int node, pri, scr;
	int isconsole;

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

#ifdef DIAGNOSTIC
	if (ra->ra_nreg < P9100_NREG) {
		printf(": expected %d registers, got only %d\n",
		    P9100_NREG, ra->ra_nreg);
		return;
	}
#endif

	sc->sc_flags = SCF_INTERNAL;
	sc->sc_mapmode = WSDISPLAYIO_MODE_EMUL;

	bcopy(ra->ra_reg, sc->sc_phys, sizeof(sc->sc_phys));

	sc->sc_ctl = mapiodev(&ra->ra_reg[P9100_REG_CTL], 0,
	    ra->ra_reg[P9100_REG_CTL].rr_len);
	sc->sc_cmd = mapiodev(&ra->ra_reg[P9100_REG_CMD], 0,
	    ra->ra_reg[P9100_REG_CMD].rr_len);
#ifdef	FIDDLE_WITH_PCI_REGISTERS
	sc->sc_pci = (struct p9100_pci *)
	    mapiodev(&ra->ra_reg[P9100_REG_CONFIG], 0,
	      ra->ra_reg[P9100_REG_CONFIG].rr_len);
#endif

	node = ra->ra_node;
	isconsole = node == fbnode;

	P9100_SELECT_SCR(sc);
	scr = P9100_READ_CTL(sc, P9000_SYSTEM_CONFIG);

	fb_setsize(&sc->sc_sunfb, 8, 800, 600, node, ca->ca_bustype);

	/*
	 * We expect the PROM to initialize us in the best 8 bit mode
	 * supported by the LCD (640x480 on 3XP, 800x600 on 3GS/3GX).
	 */
	sc->sc_lcdwidth = sc->sc_sunfb.sf_width;
	sc->sc_lcdheight = sc->sc_sunfb.sf_height;

#if NTCTRL > 0
	/*
	 * ... but it didn't if we are running on serial console.
	 * In this case, do it ourselves.
	 */
	if (!isconsole)
		p9100_initialize_ramdac(sc, sc->sc_lcdwidth, 8);
#endif

#if NTCTRL > 0
	/*
	 * We want to run the frame buffer in 8bpp mode for the emulation mode,
	 * and use a potentially better mode for the mapped (X11) mode.
	 * Eventually this will become runtime user-selectable.
	 */

	sc->sc_mapwidth = sc->sc_lcdwidth;
	sc->sc_mapheight = sc->sc_lcdheight;
	sc->sc_mapdepth = 8;

	if (sc->sc_mapwidth != sc->sc_sunfb.sf_width ||
	    sc->sc_mapdepth != sc->sc_sunfb.sf_depth)
		SET(sc->sc_flags, SCF_MAPPEDSWITCH);
#endif

	ri->ri_bits = mapiodev(&ra->ra_reg[P9100_REG_VRAM], 0,
	    sc->sc_vramsize = round_page(ra->ra_reg[P9100_REG_VRAM].rr_len));
	ri->ri_hw = sc;

	printf(": rev %x, %dx%d\n", scr & SCR_ID_MASK,
	    sc->sc_lcdwidth, sc->sc_lcdheight);

	/* Disable frame buffer interrupts */
	P9100_SELECT_SCR(sc);
	P9100_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE, IER_MASTER_ENABLE | 0);

	sc->sc_ih.ih_fun = p9100_intr;
	sc->sc_ih.ih_arg = sc;
	intr_establish(pri, &sc->sc_ih, IPL_FB, self->dv_xname);

	/*
	 * Try to get a copy of the PROM font.
	 *
	 * If we can't, we'll clear the display and switch to the 8x16 font.
	 */
	if (isconsole)
		p9100_pick_romfont(sc);

	/*
	 * Register the external video control callback with tctrl; tctrl
	 * will invoke it immediately to set the appropriate behaviour.
	 * If tctrl is not configured, simply enable external video.
	 */
#if NTCTRL > 0
	tadpole_register_extvideo(p9100_external_video, sc);
#else
	p9100_external_video(sc, 1);
#endif

	fbwscons_init(&sc->sc_sunfb, isconsole);
	fbwscons_setcolormap(&sc->sc_sunfb, p9100_setcolor);

	/*
	 * Plug-in accelerated console operations.
	 */
	p9100_ras_init(sc);

	/* enable video */
	p9100_burner(sc, 1, 0);

	if (isconsole)
		fbwscons_console_init(&sc->sc_sunfb, -1);

	fbwscons_attach(&sc->sc_sunfb, &p9100_accessops, isconsole);
}

int
p9100activate(struct device *self, int act)
{
	int ret = 0;

	switch (act) {
	case DVACT_POWERDOWN:
#if NTCTRL > 0
		p9100_prom(self);
#endif
		break;
	}

	return (ret);
}

int
p9100_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct p9100_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_cmap *cm;
#if NTCTRL > 0
	struct wsdisplay_param *dp;
#endif
	int error;

	switch (cmd) {

	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_SB_P9100;
		break;

	case WSDISPLAYIO_SMODE:
		sc->sc_mapmode = *(u_int *)data;
		switch (sc->sc_mapmode) {
		case WSDISPLAYIO_MODE_DUMBFB:
		case WSDISPLAYIO_MODE_MAPPED:
#if NTCTRL > 0
			if (ISSET(sc->sc_flags, SCF_MAPPEDSWITCH))
				p9100_initialize_ramdac(sc,
				    sc->sc_mapwidth, sc->sc_mapdepth);
#endif
			break;
		case WSDISPLAYIO_MODE_EMUL:
#if NTCTRL > 0
			if (ISSET(sc->sc_flags, SCF_MAPPEDSWITCH))
				p9100_initialize_ramdac(sc, sc->sc_lcdwidth, 8);
#endif
			fbwscons_setcolormap(&sc->sc_sunfb, p9100_setcolor);
			/* Restore proper acceleration state as well */
			p9100_ras_init(sc);
			break;
		}
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
#if NTCTRL > 0
		if (ISSET(sc->sc_flags, SCF_MAPPEDSWITCH)) {
			wdf->width = sc->sc_mapwidth;
			wdf->height = sc->sc_mapheight;
			wdf->depth  = sc->sc_mapdepth;
			wdf->cmsize = sc->sc_mapdepth == 8 ? 256 : 0;
		} else
#endif
		{
			wdf->width  = sc->sc_lcdwidth;
			wdf->height = sc->sc_lcdheight;
			wdf->depth  = 8;
			wdf->cmsize = 256;
		}
		break;

	case WSDISPLAYIO_LINEBYTES:
#if NTCTRL > 0
		if (ISSET(sc->sc_flags, SCF_MAPPEDSWITCH))
			*(u_int *)data = sc->sc_mapwidth *
			    (sc->sc_mapdepth / 8);
		else
#endif
			*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = bt_getcmap(&sc->sc_cmap, cm);
		if (error)
			return (error);
		break;

	case WSDISPLAYIO_PUTCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = bt_putcmap(&sc->sc_cmap, cm);
		if (error)
			return (error);
		p9100_loadcmap_deferred(sc, cm->index, cm->count);
		break;

#if NTCTRL > 0
	case WSDISPLAYIO_GETPARAM:
		dp = (struct wsdisplay_param *)data;

		switch (dp->param) {
		case WSDISPLAYIO_PARAM_BRIGHTNESS:
			dp->min = 0;
			dp->max = 255;
			dp->curval = tadpole_get_brightness();
			break;
		case WSDISPLAYIO_PARAM_BACKLIGHT:
			dp->min = 0;
			dp->max = 1;
			if (ISSET(sc->sc_flags, SCF_INTERNAL))
				dp->curval =
				    tadpole_get_video() & TV_ON ? 1 : 0;
			else
				dp->curval = 0;
			break;
		default:
			return (-1);
		}
		break;

	case WSDISPLAYIO_SETPARAM:
		dp = (struct wsdisplay_param *)data;

		switch (dp->param) {
		case WSDISPLAYIO_PARAM_BRIGHTNESS:
			tadpole_set_brightness(dp->curval);
			break;
		case WSDISPLAYIO_PARAM_BACKLIGHT:
			if (ISSET(sc->sc_flags, SCF_INTERNAL))
				tadpole_set_video(dp->curval);
			break;
		default:
			return (-1);
		}
		break;
#endif	/* NTCTRL > 0 */

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return (-1);	/* not supported yet */
	}

	return (0);
}

paddr_t
p9100_mmap(void *v, off_t offset, int prot)
{
	struct p9100_softc *sc = v;
	struct rom_reg *rr;

	if ((offset & PAGE_MASK) != 0)
		return (-1);

	switch (sc->sc_mapmode) {
	case WSDISPLAYIO_MODE_MAPPED:
		/*
		 * We provide the following mapping:
		 * 000000 - 0000ff  control registers
		 * 002000 - 003fff  command registers
		 * 800000 - 9fffff  vram
		 */
		rr = &sc->sc_phys[P9100_REG_CTL];
		if (offset >= 0 && offset < rr->rr_len)
			break;
		offset -= 0x2000;
		rr = &sc->sc_phys[P9100_REG_CMD];
		if (offset >= 0 && offset < rr->rr_len)
			break;
		offset -= (0x800000 - 0x2000);
		/* FALLTHROUGH */
	case WSDISPLAYIO_MODE_DUMBFB:
		rr = &sc->sc_phys[P9100_REG_VRAM];
		if (offset >= 0 && offset < sc->sc_vramsize)
			break;
		/* FALLTHROUGH */
	default:
		return (-1);
	}

	return (REG2PHYS(rr, offset) | PMAP_NC);
}

void
p9100_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct p9100_softc *sc = v;
	union bt_cmap *bcm = &sc->sc_cmap;

	bcm->cm_map[index][0] = r;
	bcm->cm_map[index][1] = g;
	bcm->cm_map[index][2] = b;
	p9100_loadcmap_immediate(sc, index, 1);
}

void
p9100_loadcmap_immediate(struct p9100_softc *sc, u_int start, u_int ncolors)
{
	u_char *p;

	P9100_SELECT_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_WRADDR, start);
	P9100_FLUSH_DAC(sc);

	for (p = sc->sc_cmap.cm_map[start], ncolors *= 3; ncolors-- > 0; p++) {
		P9100_SELECT_DAC(sc);
		P9100_WRITE_RAMDAC(sc, IBM525_DATA, (*p));
		P9100_FLUSH_DAC(sc);
	}
}

static __inline__ void
p9100_loadcmap_deferred(struct p9100_softc *sc, u_int start, u_int ncolors)
{
	/* Schedule an interrupt for next retrace */
	P9100_SELECT_SCR(sc);
	P9100_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE,
	    IER_MASTER_ENABLE | IER_MASTER_INTERRUPT |
	    IER_VBLANK_ENABLE | IER_VBLANK_INTERRUPT);
}

u_int
p9100_read_ramdac(struct p9100_softc *sc, u_int reg)
{
	P9100_SELECT_DAC(sc);

	P9100_WRITE_RAMDAC(sc, IBM525_IDXLOW, (reg & 0xff));
	P9100_FLUSH_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_IDXHIGH, ((reg >> 8) & 0xff));
	P9100_FLUSH_DAC(sc);
	return (P9100_READ_RAMDAC(sc, IBM525_REGDATA));
}

void
p9100_write_ramdac(struct p9100_softc *sc, u_int reg, u_int value)
{
	P9100_SELECT_DAC(sc);

	P9100_WRITE_RAMDAC(sc, IBM525_IDXLOW, (reg & 0xff));
	P9100_FLUSH_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_IDXHIGH, ((reg >> 8) & 0xff));
	P9100_FLUSH_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_REGDATA, value);
	P9100_FLUSH_DAC(sc);
}

void
p9100_burner(void *v, u_int on, u_int flags)
{
	struct p9100_softc *sc = v;
	u_int32_t vcr;
	int s;

	s = splhigh();
	P9100_SELECT_VCR(sc);
	vcr = P9100_READ_CTL(sc, P9000_SRTC1);
	if (on)
		vcr |= SRTC1_VIDEN;
	else
		vcr &= ~SRTC1_VIDEN;
	P9100_WRITE_CTL(sc, P9000_SRTC1, vcr);
#if NTCTRL > 0
	if (ISSET(sc->sc_flags, SCF_INTERNAL))
		tadpole_set_video(on);
#endif
	splx(s);
}

int
p9100_intr(void *v)
{
	struct p9100_softc *sc = v;

	if (P9100_READ_CTL(sc, P9000_INTERRUPT) & IER_VBLANK_INTERRUPT) {
		p9100_loadcmap_immediate(sc, 0, 256);

		/* Disable further interrupts now */
		/* P9100_SELECT_SCR(sc); */
		P9100_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE,
		    IER_MASTER_ENABLE | 0);

		/* Clear interrupt condition */
		P9100_WRITE_CTL(sc, P9000_INTERRUPT,
		    IER_VBLANK_ENABLE | 0);

		return (1);
	}

	return (0);
}

/*
 * Accelerated text console code
 */

static int p9100_drain(struct p9100_softc *);

static int
p9100_drain(struct p9100_softc *sc)
{
	u_int i;

	for (i = 10000; i !=0; i--) {
		if ((P9100_READ_CMD(sc, P9000_PE_STATUS) &
		    (STATUS_QUAD_BUSY | STATUS_BLIT_BUSY)) == 0)
			break;
	}

	return (i);
}

void
p9100_ras_init(struct p9100_softc *sc)
{

	if (p9100_drain(sc) == 0)
		return;

	sc->sc_sunfb.sf_ro.ri_ops.copycols = p9100_ras_copycols;
	sc->sc_sunfb.sf_ro.ri_ops.copyrows = p9100_ras_copyrows;
	sc->sc_sunfb.sf_ro.ri_ops.erasecols = p9100_ras_erasecols;
	sc->sc_sunfb.sf_ro.ri_ops.eraserows = p9100_ras_eraserows;
	sc->sc_sunfb.sf_ro.ri_do_cursor = p9100_ras_do_cursor;

	/*
	 * Setup safe defaults for the parameter and drawing engines, in
	 * order to minimize the operations to do for ri_ops.
	 */

	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_DRAWMODE,
	    DM_PICK_CONTROL | 0 | DM_BUFFER_CONTROL | DM_BUFFER_ENABLE0);

	P9100_WRITE_CMD(sc, P9000_DE_PATTERN_ORIGIN_X, 0);
	P9100_WRITE_CMD(sc, P9000_DE_PATTERN_ORIGIN_Y, 0);
	/* enable all planes */
	P9100_WRITE_CMD(sc, P9000_DE_PLANEMASK, 0xffffffff);

	/* Unclip */
	P9100_WRITE_CMD(sc, P9000_DE_WINMIN, 0);
	P9100_WRITE_CMD(sc, P9000_DE_WINMAX,
	    P9000_COORDS(sc->sc_sunfb.sf_width - 1, sc->sc_sunfb.sf_height - 1));

	P9100_SELECT_DE_HIGH(sc);
	P9100_WRITE_CMD(sc, P9100_DE_B_WINMIN, 0);
	P9100_WRITE_CMD(sc, P9100_DE_B_WINMAX,
	    P9000_COORDS(sc->sc_sunfb.sf_width - 1, sc->sc_sunfb.sf_height - 1));

	P9100_SELECT_PE(sc);
	P9100_WRITE_CMD(sc, P9000_PE_WINOFFSET, 0);
	P9100_WRITE_CMD(sc, P9000_PE_INDEX, 0);
	P9100_WRITE_CMD(sc, P9000_PE_WINMIN, 0);
	P9100_WRITE_CMD(sc, P9000_PE_WINMAX,
	    P9000_COORDS(sc->sc_sunfb.sf_width - 1, sc->sc_sunfb.sf_height - 1));
}

int
p9100_ras_copycols(void *v, int row, int src, int dst, int n)
{
	struct rasops_info *ri = v;
	struct p9100_softc *sc = ri->ri_hw;

	n *= ri->ri_font->fontwidth;
	n--;
	src *= ri->ri_font->fontwidth;
	src += ri->ri_xorigin;
	dst *= ri->ri_font->fontwidth;
	dst += ri->ri_xorigin;
	row *= ri->ri_font->fontheight;
	row += ri->ri_yorigin;

	p9100_drain(sc);
	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9100_RASTER_SRC & P9100_RASTER_MASK);

	P9100_SELECT_COORD(sc, P9000_DC_COORD(0));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(0) + P9000_COORD_XY,
	    P9000_COORDS(src, row));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(1) + P9000_COORD_XY,
	    P9000_COORDS(src + n, row + ri->ri_font->fontheight - 1));
	P9100_SELECT_COORD(sc, P9000_DC_COORD(2));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(2) + P9000_COORD_XY,
	    P9000_COORDS(dst, row));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(3) + P9000_COORD_XY,
	    P9000_COORDS(dst + n, row + ri->ri_font->fontheight - 1));

	sc->sc_junk = P9100_READ_CMD(sc, P9000_PE_BLIT);

	p9100_drain(sc);

	return 0;
}

int
p9100_ras_copyrows(void *v, int src, int dst, int n)
{
	struct rasops_info *ri = v;
	struct p9100_softc *sc = ri->ri_hw;

	n *= ri->ri_font->fontheight;
	n--;
	src *= ri->ri_font->fontheight;
	src += ri->ri_yorigin;
	dst *= ri->ri_font->fontheight;
	dst += ri->ri_yorigin;

	p9100_drain(sc);
	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9100_RASTER_SRC & P9100_RASTER_MASK);

	P9100_SELECT_COORD(sc, P9000_DC_COORD(0));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(0) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin, src));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(1) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth - 1, src + n));
	P9100_SELECT_COORD(sc, P9000_DC_COORD(2));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(2) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin, dst));
	P9100_WRITE_CMD(sc, P9000_DC_COORD(3) + P9000_COORD_XY,
	    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth - 1, dst + n));

	sc->sc_junk = P9100_READ_CMD(sc, P9000_PE_BLIT);

	p9100_drain(sc);

	return 0;
}

int
p9100_ras_erasecols(void *v, int row, int col, int n, long int attr)
{
	struct rasops_info *ri = v;
	struct p9100_softc *sc = ri->ri_hw;
	int fg, bg;

	ri->ri_ops.unpack_attr(v, attr, &fg, &bg, NULL);
	bg = ri->ri_devcmap[bg];

	n *= ri->ri_font->fontwidth;
	col *= ri->ri_font->fontwidth;
	col += ri->ri_xorigin;
	row *= ri->ri_font->fontheight;
	row += ri->ri_yorigin;

	p9100_drain(sc);
	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9100_RASTER_PATTERN & P9100_RASTER_MASK);
	P9100_WRITE_CMD(sc, P9100_DE_COLOR0, P9100_COLOR8(bg));

	P9100_SELECT_COORD(sc, P9000_LC_RECT);
	P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col, row));
	P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col + n, row + ri->ri_font->fontheight));

	sc->sc_junk = P9100_READ_CMD(sc, P9000_PE_QUAD);

	p9100_drain(sc);

	return 0;
}

int
p9100_ras_eraserows(void *v, int row, int n, long int attr)
{
	struct rasops_info *ri = v;
	struct p9100_softc *sc = ri->ri_hw;
	int fg, bg;

	ri->ri_ops.unpack_attr(v, attr, &fg, &bg, NULL);
	bg = ri->ri_devcmap[bg];

	p9100_drain(sc);
	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_RASTER,
	    P9100_RASTER_PATTERN & P9100_RASTER_MASK);
	P9100_WRITE_CMD(sc, P9100_DE_COLOR0, P9100_COLOR8(bg));

	P9100_SELECT_COORD(sc, P9000_LC_RECT);
	if (n == ri->ri_rows && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
		P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(0, 0));
		P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_width, ri->ri_height));
	} else {
		n *= ri->ri_font->fontheight;
		row *= ri->ri_font->fontheight;
		row += ri->ri_yorigin;

		P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_xorigin, row));
		P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
		    P9000_COORDS(ri->ri_xorigin + ri->ri_emuwidth, row + n));
	}

	sc->sc_junk = P9100_READ_CMD(sc, P9000_PE_QUAD);

	p9100_drain(sc);

	return 0;
}

int
p9100_ras_do_cursor(struct rasops_info *ri)
{
	struct p9100_softc *sc = ri->ri_hw;
	int row, col;

	row = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
	col = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;

	p9100_drain(sc);

	P9100_SELECT_DE_LOW(sc);
	P9100_WRITE_CMD(sc, P9000_DE_RASTER,
	    (P9100_RASTER_PATTERN ^ P9100_RASTER_DST) & P9100_RASTER_MASK);
	P9100_WRITE_CMD(sc, P9100_DE_COLOR0,
	    P9100_COLOR8(ri->ri_devcmap[WSCOL_BLACK]));

	P9100_SELECT_COORD(sc, P9000_LC_RECT);
	P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col, row));
	P9100_WRITE_CMD(sc, P9000_LC_RECT + P9000_COORD_XY,
	    P9000_COORDS(col + ri->ri_font->fontwidth,
	        row + ri->ri_font->fontheight));

	sc->sc_junk = P9100_READ_CMD(sc, P9000_PE_QUAD);

	p9100_drain(sc);

	return 0;
}

/*
 * PROM font managment
 */

#define	ROMFONTNAME	"p9100_romfont"
struct wsdisplay_font p9100_romfont = {
	ROMFONTNAME,
	0,
	0, 256,
	WSDISPLAY_FONTENC_ISO,	/* should check the `character-set' property */
	0, 0, 0,
	WSDISPLAY_FONTORDER_L2R,
	WSDISPLAY_FONTORDER_L2R,
	NULL,
	NULL
};

void
p9100_pick_romfont(struct p9100_softc *sc)
{
	struct rasops_info *ri = &sc->sc_sunfb.sf_ro;
	int *fontwidth, *fontheight, *fontstride;
	u_int8_t **fontaddr;
	char buf[200];

	/*
	 * This code currently only works for PROM >= 2.9; see
	 * autoconf.c romgetcursoraddr() for details.
	 */
	if (promvec->pv_romvec_vers < 2 || promvec->pv_printrev < 0x00020009)
		return;

	/*
	 * Get the PROM font metrics and address
	 */
	if (snprintf(buf, sizeof buf, "stdout @ is my-self "
	    "addr char-height %lx ! addr char-width %lx ! "
	    "addr font-base %lx ! addr fontbytes %lx !",
	    (vaddr_t)&fontheight, (vaddr_t)&fontwidth,
	    (vaddr_t)&fontaddr, (vaddr_t)&fontstride) >= sizeof buf)
		return;
	fontheight = fontwidth = fontstride = NULL;
	fontaddr = NULL;
	rominterpret(buf);

	if (fontheight == NULL || fontwidth == NULL || fontstride == NULL ||
	    fontaddr == NULL || *fontheight == 0 || *fontwidth == 0 ||
	    *fontstride < howmany(*fontwidth, NBBY) ||
	    *fontstride > 4 /* paranoia */)
		return;

	p9100_romfont.fontwidth = *fontwidth;
	p9100_romfont.fontheight = *fontheight;
	p9100_romfont.stride = *fontstride;
	p9100_romfont.data = *fontaddr;

#ifdef DEBUG
	printf("%s: PROM font %dx%d-%d @%p",
	    sc->sc_sunfb.sf_dev.dv_xname, *fontwidth, *fontheight,
	    *fontstride, *fontaddr);
#endif

	/*
	 * Build and add a wsfont structure
	 */
	wsfont_init();	/* if not done before */
	if (wsfont_add(&p9100_romfont, 0) != 0)
		return;

	/*
	 * Select this very font in our rasops structure
	 */
	ri->ri_wsfcookie = wsfont_find(ROMFONTNAME, 0, 0, 0);
	if (wsfont_lock(ri->ri_wsfcookie, &ri->ri_font,
	    WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R) <= 0)
		ri->ri_wsfcookie = 0;
}

/*
 * External video control
 */

void
p9100_external_video(void *v, int on)
{
	struct p9100_softc *sc = v;
	int s;

	s = splhigh();

	if (on) {
		p9100_write_ramdac(sc, IBM525_POWER,
		    p9100_read_ramdac(sc, IBM525_POWER) & ~P_DAC_PWR_DISABLE);
		SET(sc->sc_flags, SCF_EXTERNAL);
	} else {
		p9100_write_ramdac(sc, IBM525_POWER,
		    p9100_read_ramdac(sc, IBM525_POWER) | P_DAC_PWR_DISABLE);
		CLR(sc->sc_flags, SCF_EXTERNAL);
	}

	splx(s);
}

/*
 * Video mode programming
 *
 * All magic values come from s3gxtrmb.pdf.
 */

#if NTCTRL > 0

/* IBM RGB525 registers and values */

static const u_int8_t p9100_dacreg[] = {
	IBM525_MISC1,
	IBM525_MISC2,
	IBM525_MISC3,
	IBM525_MISC4,
	IBM525_MISC_CLOCK,
	IBM525_SYNC,
	IBM525_HSYNC_POS,
	IBM525_POWER,
	IBM525_DAC_OP,
	IBM525_PALETTE,
	IBM525_PIXEL,
	IBM525_PF8,
	IBM525_PF16,
	IBM525_PF24,
	IBM525_PF32,
	IBM525_PLL1,
	IBM525_PLL2,
	IBM525_PLL_FIXED_REF,
	IBM525_SYSCLK,
	IBM525_PLL_REF_DIV,
	IBM525_PLL_VCO_DIV,
	0
};

static u_int8_t p9100_dacval[] = {
	M1_SENSE_DISABLE | M1_VRAM_64,
	M2_PCLK_PLL | M2_PALETTE_8 | M2_MODE_VRAM,
	0,
	0,				/* will be computed */
	MC_B24P_SCLK | MC_PLL_ENABLE,	/* will be modified */
	S_HSYN_NORMAL | S_VSYN_NORMAL,
	0,
	0,				/* will be computed */
	DO_FAST_SLEW,
	0,
	0,				/* will be computed */
	PF8_INDIRECT,
	PF16_DIRECT | PF16_LINEAR | PF16_565,
	PF24_DIRECT,
	PF32_DIRECT,
	P1_CLK_REF | P1_SRC_EXT_F | P1_SRC_DIRECT_F,
	0,	/* F0, will be set before */
	5,
	SC_ENABLE,
	5,
	MHZ_TO_PLL(50)
};

/* Power 9100 registers and values */

static const u_int32_t p9100_reg[] = {
	P9000_HTR,
	P9000_HSRE,
	P9000_HBRE,
	P9000_HBFE,
	P9000_HCP,
	P9000_VL,
	P9000_VSRE,
	P9000_VBRE,
	P9000_VBFE,
	P9000_VCP,
	0
};

static const u_int32_t p9100_val_800_32[] = {
	0x1f3, 0x023, 0x053, 0x1e3, 0x000, 0x271, 0x002, 0x016, 0x26e, 0x000
};
#if 0	/* No X server for this mode, yet */
static const u_int32_t p9100_val_800_24[] = {
	0x176, 0x01a, 0x03d, 0x169, 0x000, 0x271, 0x002, 0x016, 0x26e, 0x000
};
#endif
static const u_int32_t p9100_val_800_8[] = {
	0x07c, 0x008, 0x011, 0x075, 0x000, 0x271, 0x002, 0x016, 0x26e, 0x000
};
static const u_int32_t p9100_val_640_32[] = {
	0x18f, 0x02f, 0x043, 0x183, 0x000, 0x205, 0x003, 0x022, 0x202, 0x000
};
static const u_int32_t p9100_val_640_8[] = {
	0x063, 0x00b, 0x00d, 0x05d, 0x000, 0x205, 0x003, 0x022, 0x202, 0x000
};
static const u_int32_t p9100_val_1024_8[] = {
	0x0a7, 0x019, 0x022, 0x0a2, 0x000, 0x325, 0x003, 0x023, 0x323, 0x000
};

void
p9100_initialize_ramdac(struct p9100_softc *sc, u_int width, u_int depth)
{
	int s;
	const u_int8_t *dacregp, *dacvalp;
	const u_int32_t *p9regp, *p9valp;
	u_int8_t pllclk, dacval;
	u_int32_t scr;

	/*
	 * XXX Switching to a low-res 8bpp mode does not work correctly
	 * XXX unless coming from an high-res 8bpp mode, and I have
	 * XXX no idea why.
	 * XXX Of course, this mean that we can't reasonably use this
	 * XXX routine unless NTCTRL > 0.
	 */
	if (depth == 8 && width != 1024)
		p9100_initialize_ramdac(sc, 1024, 8);

	switch (width) {
	case 1024:
		p9valp = p9100_val_1024_8;
		pllclk = MHZ_TO_PLL(65);
		/* 1024 bytes scanline */
		scr = SCR_SC(0, 0, 0, 1) | SCR_PIXEL_8BPP;
		break;
	default:
		/* FALLTHROUGH */
	case 800:
		switch (depth) {
		case 32:
			p9valp = p9100_val_800_32;
			/* 3200 = 128 + 1024 + 2048 bytes scanline */
			scr = SCR_SC(3, 6, 7, 0) |
			    SCR_PIXEL_32BPP | SCR_SWAP_WORDS | SCR_SWAP_BYTES;
			break;
#if 0
		case 24:
			p9valp = p9100_val_800_24;
			/* 2400 = 32 + 64 + 256 + 2048 bytes scanline */
			scr = SCR_SC(1, 2, 4, 2) | SCR_PIXEL_24BPP;
			break;
#endif
		default:
		case 8:
			p9valp = p9100_val_800_8;
			/* 800 = 32 + 256 + 512 bytes scanline */
			scr = SCR_SC(1, 4, 5, 0) | SCR_PIXEL_8BPP;
			break;
		}
		pllclk = MHZ_TO_PLL(36);
		break;
	case 640:
		switch (depth) {
		case 32:
			p9valp = p9100_val_640_32;
			/* 2560 = 512 + 2048 bytes scanline */
			scr = SCR_SC(5, 7, 0, 0) |
			    SCR_PIXEL_32BPP | SCR_SWAP_WORDS | SCR_SWAP_BYTES;
			break;
		default:
		case 8:
			p9valp = p9100_val_640_8;
			/* 640 = 128 + 512 bytes scanline */
			scr = SCR_SC(3, 5, 0, 0) | SCR_PIXEL_8BPP;
			break;
		}
		pllclk = MHZ_TO_PLL(25);
		break;
	}
	dacvalp = p9100_dacval;

	s = splhigh();

#ifdef	FIDDLE_WITH_PCI_REGISTERS
	/*
	 * Magical initialization sequence, from s3gxtrmb.pdf.
	 * DANGER! Sometimes freezes the machine solid, cause unknown.
	 */
	sc->sc_pci->address = 0x13000000;
	sc->sc_pci->data = 0;
	sc->sc_pci->address = 0x30000000;
	sc->sc_pci->data = 0;
	sc->sc_pci->address = 0x41000000;
	sc->sc_pci->data = 0;	/* No register mapping at a0000 */
	sc->sc_pci->address = 0x04000000;
	sc->sc_pci->data = 0xa3000000;
#endif

	/*
	 * Initialize the RAMDAC
	 */
	P9100_SELECT_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_PIXMASK, 0xff);
	P9100_FLUSH_DAC(sc);
	P9100_WRITE_RAMDAC(sc, IBM525_IDXCONTROL, 0x00);
	P9100_FLUSH_DAC(sc);

	p9100_write_ramdac(sc, IBM525_F(0), pllclk);
	for (dacregp = p9100_dacreg; *dacregp != 0; dacregp++, dacvalp++) {
		switch (*dacregp) {
		case IBM525_MISC4:
			dacval =  pllclk >= MHZ_TO_PLL(50) ?
			    M4_FAST : M4_INVERT_DCLK;
			break;
		case IBM525_MISC_CLOCK:
			dacval = *dacvalp & ~MC_DDOT_DIV_MASK;
			switch (depth) {
			case 32:
				dacval |= MC_DDOT_DIV_2;
				break;
			case 16:
				dacval |= MC_DDOT_DIV_4;
				break;
			default:
			case 24:
			case 8:
				dacval |= MC_DDOT_DIV_8;
				break;
			}
			break;
		case IBM525_POWER:
			if (depth == 24)
				dacval = 0;
			else
				dacval = P_SCLK_DISABLE;
			break;
		case IBM525_PIXEL:
			switch (depth) {
			case 32:
				dacval = PIX_32BPP;
				break;
			case 24:
				dacval = PIX_24BPP;
				break;
			case 16:
				dacval = PIX_16BPP;
				break;
			default:
			case 8:
				dacval = PIX_8BPP;
				break;
			}
			break;
		default:
			dacval = *dacvalp;
			break;
		}
		p9100_write_ramdac(sc, *dacregp, dacval);
	}

	/*
	 * Initialize the Power 9100
	 */

	P9100_SELECT_SCR(sc);
	P9100_WRITE_CTL(sc, P9000_SYSTEM_CONFIG, scr);
	P9100_SELECT_VCR(sc);
	P9100_WRITE_CTL(sc, P9000_SRTC1,
	    SRTC1_VSYNC_INTERNAL | SRTC1_HSYNC_INTERNAL | SRTC1_VIDEN | 0x03);
	P9100_WRITE_CTL(sc, P9000_SRTC2, 0x05);
	/* need to wait a bit before VRAM control registers are accessible */
	delay(3000);
	P9100_SELECT_VRAM(sc);
	P9100_WRITE_CTL(sc, P9000_MCR, 0xc808007d);
	delay(3000);

	P9100_SELECT_VCR(sc);
	for (p9regp = p9100_reg; *p9regp != 0; p9regp++, p9valp++)
		P9100_WRITE_CTL(sc, *p9regp, *p9valp);

	P9100_SELECT_VRAM(sc);
	P9100_WRITE_CTL(sc, P9000_REFRESH_PERIOD, 0x3ff);

	/* Disable frame buffer interrupts */
	P9100_SELECT_SCR(sc);
	P9100_WRITE_CTL(sc, P9000_INTERRUPT_ENABLE, IER_MASTER_ENABLE | 0);

	/*
	 * Enable internal video... (it's a kind of magic)
	 */
	p9100_write_ramdac(sc, IBM525_MISC4,
	    p9100_read_ramdac(sc, IBM525_MISC4) | 0xc0);

	/*
	 * ... unless it does not fit.
	 */
	if (width != sc->sc_lcdwidth) {
		CLR(sc->sc_flags, SCF_INTERNAL);
		tadpole_set_video(0);
	} else {
		SET(sc->sc_flags, SCF_INTERNAL);
		tadpole_set_video(1);
	}

	p9100_external_video(sc, ISSET(sc->sc_flags, SCF_EXTERNAL));

	splx(s);
}

void
p9100_prom(void *v)
{
	struct p9100_softc *sc = v;

	if (ISSET(sc->sc_flags, SCF_MAPPEDSWITCH) &&
	    sc->sc_mapmode != WSDISPLAYIO_MODE_EMUL) {
		p9100_initialize_ramdac(sc, sc->sc_lcdwidth, 8);
		fbwscons_setcolormap(&sc->sc_sunfb, p9100_setcolor);
		p9100_ras_init(sc);
	}
}
#endif	/* NTCTRL > 0 */

license: unknown


code:
/*	$OpenBSD: pfourreg.h,v 1.5 2003/06/02 18:47:58 deraadt Exp $	*/
/*	$NetBSD: pfourreg.h,v 1.1 1996/02/27 22:09:36 thorpej Exp $	*/

/*
 * Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.
 * Copyright (c) 1995 Theo de Raadt
 * 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.
 * 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.
 */

/*
 * pfour framebuffer registers.
 */

/* Offset of bwtwo framebuffer from pfour register */
#define PFOUR_BW_OFF		0x00100000

/* Offsets for color framebuffers */
#define PFOUR_COLOR_OFF_OVERLAY	0x00100000
#define PFOUR_COLOR_OFF_ENABLE	0x00300000
#define PFOUR_COLOR_OFF_COLOR	0x00500000
#define PFOUR_COLOR_OFF_END	0x00700000
#define PFOUR_COLOR_OFF_CMAP	0xfff00000	/* (-0x00100000) */

#define PFOUR_REG_DIAG		0x80
#define PFOUR_REG_READBACKCLR	0x40
#define PFOUR_REG_VIDEO		0x20
#define PFOUR_REG_SYNC		0x10
#define PFOUR_REG_VTRACE	0x08
#define PFOUR_REG_INT		0x04
#define PFOUR_REG_INTCLR	0x04
#define PFOUR_REG_INTEN		0x02
#define PFOUR_REG_FIRSTHALF	0x01
#define PFOUR_REG_RESET		0x01

#define PFOUR_FBTYPE_MASK	0x7f000000
#define	PFOUR_FBTYPE(x)		((x) >> 24)

#define	PFOUR_ID_MASK		0xf0
#define PFOUR_ID(x)		(PFOUR_FBTYPE((x)) == PFOUR_ID_COLOR24 ? \
				    PFOUR_ID_COLOR24 : \
				    PFOUR_FBTYPE((x)) & PFOUR_ID_MASK)
#define PFOUR_ID_BW		0x00	/* monochrome */
#define PFOUR_ID_FASTCOLOR	0x60	/* accelerated 8-bit color */
#define PFOUR_ID_COLOR8P1	0x40	/* 8-bit color + overlay */
#define PFOUR_ID_COLOR24	0x45	/* 24-bit color + overlay */

#define PFOUR_SIZE_MASK		0x0f
#define PFOUR_SIZE(x)		(PFOUR_FBTYPE((x)) & PFOUR_SIZE_MASK)
#define PFOUR_SIZE_1152X900	0x01
#define PFOUR_SIZE_1024X1024	0x02
#define PFOUR_SIZE_1280X1024	0x03
#define PFOUR_SIZE_1600X1280	0x00
#define PFOUR_SIZE_1440X1440	0x04
#define PFOUR_SIZE_640X480	0x05

#define PFOUR_NOTPFOUR		-1


code:
/*	$OpenBSD: power.c,v 1.10 2007/07/01 19:07:45 miod Exp $	*/
/*	$NetBSD: power.c,v 1.2 1996/05/16 15:56:56 abrown Exp $ */

/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard College. All rights reserved.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Aaron Brown and
 *	Harvard University.
 *
 * 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 Harvard University
 *	and its contributors.
 * 4. 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.
 *
 */

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

#include <machine/autoconf.h>

#include <sparc/dev/power.h>

void	powerattach(struct device *, struct device *, void *);
int	powermatch(struct device *, void *, void *);

struct cfattach power_ca = {
	sizeof(struct device), powermatch, powerattach
};

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

static char power_attached = 0;
volatile u_char *power_reg;

/*
 * This is the driver for the "power" register available on some Sun4m
 * machines. This allows the machine to remove power automatically when
 * shutdown or halted or whatever.
 */

int
powermatch(struct device *parent, void *vcf, void *aux)
{
	struct confargs *ca = aux;

	if (CPU_ISSUN4M) {
		if (strcmp("power", ca->ca_ra.ra_name) == 0)
			return (1);
	}

	return (0);
}

/* ARGSUSED */
void
powerattach(struct device *parent, struct device *self, void *aux)
{
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	power_reg = mapiodev(ra->ra_reg, 0, sizeof(long));

	power_attached = 1;

	printf("\n");
}

void
auxio_powerdown()
{
	if (power_attached) {
		*POWER_REG |= POWER_OFF;
		DELAY(1000000);
	}
}


code:
/*	$OpenBSD: power.h,v 1.5 2005/03/23 17:12:24 miod Exp $ */
/*	$NetBSD: power.h,v 1.2 1996/05/16 15:56:57 abrown Exp $ */

/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard College. All rights reserved.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Aaron Brown and
 *	Harvard University.
 *
 * 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 Harvard University
 *	and its contributors.
 * 4. 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.
 *
 *
 */

/*
 * Sun-4m power register.  This register allows the computer to power itself
 * down.
 */

#define POWER_OFF	0x1	/* remove power */

#define	POWER_REG	((volatile u_char *)(power_reg))

#define	POWER_BITS	"\20\1POWEROFF"

#ifndef _LOCORE
extern volatile u_char *power_reg;
#endif

void auxio_powerdown(void);		/* power off function */


code:
/*	$OpenBSD: presto.c,v 1.26 2015/03/28 19:07:07 miod Exp $	*/
/*
 * Copyright (c) 2003, 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.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/fcntl.h>
#include <sys/stat.h>

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

struct presto_softc {
	struct	device	sc_dev;
	struct	disk	sc_dk;

	vsize_t		sc_offset;	/* reserved memory offset */
	vsize_t		sc_memsize;	/* total NVRAM size */
	caddr_t		sc_mem;		/* NVRAM kva */
	caddr_t		sc_status;	/* status register kva (MBus) */
	char		sc_model[16];	/* Prestoserve model */
};

/*
 * The beginning of the NVRAM contains a few control and status values.
 * On SBus boards, there are two distinct four-bit status values;
 * On MBus boards, these are provided in the second register mapping.
 */

#define	PSERVE_BATTERYSTATUS	0x07
#define	PSBAT_CHARGING			0x10
#define	PSBAT_CONNECTED			0x20
#define	PSBAT_FAULT			0x40

#define	PSERVE_DATASTATUS	0x0b
#define	PSDATA_EMPTY			0x00
#define	PSDATA_SAVED			0x01

/*
 * Reserved area size on SBus flavours, needs to be rounded to a sector
 * size for i/o.
 */
#define	PSERVE_RESERVED		0x0010
#define	PSERVE_OFFSET		roundup(PSERVE_RESERVED, DEV_BSIZE)

void	prestostrategy(struct buf *);
void	presto_attach(struct device *, struct device *, void *);
void	presto_getdisklabel(dev_t, struct presto_softc *, struct disklabel *, int);
int	presto_match(struct device *, void *, void *);

struct cfattach presto_ca = {
	sizeof(struct presto_softc), presto_match, presto_attach
};

struct cfdriver presto_cd = {
	NULL, "presto", DV_DULL
};

#define prestolookup(unit) (struct presto_softc *)device_lookup(&presto_cd, (unit))

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

	if (strcmp(ra->ra_name, "MMI,prestoserve") != 0 &&
	    strcmp(ra->ra_name, "SUNW,nvone") != 0)
		return 0;

	if (ra->ra_nreg < 1)
		return 0;

	/* no usable memory ? */
	if (ra->ra_nreg == 1 && ra->ra_len < PSERVE_OFFSET)
		return 0;

	return 1;
}

void
presto_attach(struct device *parent, struct device *self, void *args)
{
	struct presto_softc *sc = (struct presto_softc *)self;
	struct confargs *ca = args;
	char *model, *submodel;
	u_int8_t status;

	/* Get card parameters */
	model = getpropstring(ca->ca_ra.ra_node, "model");
	if (*model == '\0')
		submodel = "fictitious";
	else {
		submodel = memchr(model, ',', strlen(model));
		if (submodel != NULL)
			submodel++;
		else
			submodel = model;
	}
	strncpy(sc->sc_model, submodel, 16);
	sc->sc_memsize = ca->ca_ra.ra_len;

	printf(": %s\n%s: %d MB NVRAM, ", model,
	    sc->sc_dev.dv_xname, sc->sc_memsize >> 20);

	/* Map memory */
	sc->sc_mem = (void *)mapiodev(ca->ca_ra.ra_reg, 0, sc->sc_memsize);
	if (ca->ca_ra.ra_nreg == 1) {
		sc->sc_status = NULL;
	} else {
		sc->sc_status = (void *)mapiodev(&ca->ca_ra.ra_reg[1], 0,
		    ca->ca_ra.ra_reg[1].rr_len);
	}

	/*
	 * Clear the ``disconnect battery'' bit.
	 */
	if (sc->sc_status == NULL)
		*(u_int8_t *)(sc->sc_mem + PSERVE_BATTERYSTATUS) = 0x00;
	else
		*(u_int8_t *)sc->sc_status &= 0x0f;

	/*
	 * Clear the ``unflushed data'' status. This way, if the card is
	 * reused under SunOS, the system will not try to flush whatever
	 * data the user put in the nvram...
	 */
	if (sc->sc_status == NULL)
		*(u_int8_t *)(sc->sc_mem + PSERVE_DATASTATUS) = 0x00;
	else
		*(u_int8_t *)sc->sc_status &= 0xf0;

	/*
	 * Decode battery status
	 */
	if (sc->sc_status == NULL)
		status = *(u_int8_t *)(sc->sc_mem + PSERVE_BATTERYSTATUS);
	else
		status = *(u_int8_t *)sc->sc_status;
	printf("battery status %02x ", status);
	if (ISSET(status, PSBAT_FAULT)) {
		printf("(non-working)");
	} else if (ISSET(status, PSBAT_CONNECTED)) {
		if (ISSET(status, PSBAT_CHARGING))
			printf("(charging)");
		else
			printf("(ok)");
	} else
		printf("(disabled)");
	printf("\n");

#ifdef DEBUG
	if (sc->sc_status == NULL) {
		printf("%s: status codes %02.2x, %02.2x, %02.2x, %02.2x\n",
		    sc->sc_dev.dv_xname,
		    *(u_int8_t *)(sc->sc_mem + 0x03),
		    *(u_int8_t *)(sc->sc_mem + 0x07),
		    *(u_int8_t *)(sc->sc_mem + 0x0b),
		    *(u_int8_t *)(sc->sc_mem + 0x0f));
	}
#endif

	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
	if (sc->sc_status == NULL)
		sc->sc_offset = PSERVE_OFFSET;
	else
		sc->sc_offset = 0;
	disk_attach(&sc->sc_dev, &sc->sc_dk);
}

/*
 * Block device i/o operations
 */

int
prestodump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
	/*
	 * A dump to nvram is theoretically possible, but its size is
	 * very likely to be WAY too small.
	 */
	return (ENXIO);
}

daddr_t
prestosize(dev_t dev)
{
	struct presto_softc *sc;
	daddr_t size;
	int part;

	sc = prestolookup(DISKUNIT(dev));
	if (sc == NULL)
		return (-1);

	part = DISKPART(dev);
	if (part >= sc->sc_dk.dk_label->d_npartitions)
		size = -1;
	else
		size = DL_SECTOBLK(sc->sc_dk.dk_label,
		    DL_GETPSIZE(&sc->sc_dk.dk_label->d_partitions[part]));

	device_unref(&sc->sc_dev);
	return (size);
}

int
prestoopen(dev_t dev, int flag, int fmt, struct proc *proc)
{
	struct presto_softc *sc;
	int error;

	sc = prestolookup(DISKUNIT(dev));
	if (sc == NULL)
		return (ENXIO);

	/* read the disk label */
	presto_getdisklabel(dev, sc, sc->sc_dk.dk_label, 0);

	/* only allow valid partitions */
	error = disk_openpart(&sc->sc_dk, DISKPART(dev), fmt, 1);

	device_unref(&sc->sc_dev);
	return (error);
}

int
prestoclose(dev_t dev, int flag, int fmt, struct proc *proc)
{
	struct presto_softc *sc;

	sc = prestolookup(DISKUNIT(dev));
	if (sc == NULL)
		return (ENXIO);

	disk_closepart(&sc->sc_dk, DISKPART(dev), fmt);

	device_unref(&sc->sc_dev);
	return (0);
}

int
prestoread(dev_t dev, struct uio *uio, int flags)
{
	return (physio(prestostrategy, dev, B_READ, minphys, uio));
}

int
prestowrite(dev_t dev, struct uio *uio, int flags)
{
	return (physio(prestostrategy, dev, B_WRITE, minphys, uio));
}

void
prestostrategy(struct buf *bp)
{
	struct presto_softc *sc;
	size_t offset, count;
	int s;

	sc = prestolookup(DISKUNIT(bp->b_dev));

	/* Sort rogue requests out */
	if (sc == NULL) {
		bp->b_error = EINVAL;
		goto bad;
	}

	/* Validate the request. */
	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
		goto done;

	/* Bound the request size, then move data between buf and nvram */
	offset = (bp->b_blkno << DEV_BSHIFT) + sc->sc_offset;
	count = bp->b_bcount;
	if (count > (sc->sc_memsize - offset))
		count = (sc->sc_memsize - offset);
	if (ISSET(bp->b_flags, B_READ))
		bcopy(sc->sc_mem + offset, bp->b_data, count);
	else
		bcopy(bp->b_data, sc->sc_mem + offset, count);
	bp->b_resid = bp->b_bcount - count;
	goto done;

 bad:
	bp->b_flags |= B_ERROR;
	bp->b_resid = bp->b_bcount;
 done:
	s = splbio();
	biodone(bp);
	splx(s);
	if (sc != NULL)
		device_unref(&sc->sc_dev);
}

int
prestoioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *proc)
{
	struct presto_softc *sc;
	int error = 0;

	sc = prestolookup(DISKUNIT(dev));

	switch (cmd) {
	case DIOCGPDINFO:
		presto_getdisklabel(dev, sc, (struct disklabel *)data, 1);
		break;

	case DIOCGDINFO:
		*(struct disklabel *)data = *sc->sc_dk.dk_label;
		break;

	case DIOCWDINFO:
	case DIOCSDINFO:
		if ((flag & FWRITE) == 0) {
			error = EBADF;
			break;
		}

		error = setdisklabel(sc->sc_dk.dk_label,
		    (struct disklabel *)data, sc->sc_dk.dk_openmask);
		if (error == 0) {
			if (cmd == DIOCWDINFO)
				error = writedisklabel(DISKLABELDEV(dev),
				    prestostrategy, sc->sc_dk.dk_label);
		}
		break;

	default:
		error = EINVAL;
		break;
	}

	device_unref(&sc->sc_dev);
	return (error);
}

/*
 * Read the disklabel. If none is present, use a fictitious one instead.
 */
void
presto_getdisklabel(dev_t dev, struct presto_softc *sc, struct disklabel *lp,
    int spoofonly)
{
	bzero(sc->sc_dk.dk_label, sizeof(struct disklabel));

	lp->d_secsize = DEV_BSIZE;
	lp->d_ntracks = 1;
	lp->d_nsectors = 32;
	DL_SETDSIZE(lp, (sc->sc_memsize - sc->sc_offset) >> DEV_BSHIFT);
	lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_nsectors;
	lp->d_secpercyl = lp->d_nsectors;

	strncpy(lp->d_typename, "Prestoserve", 16);
	lp->d_type = DTYPE_SCSI;	/* what better to put here? */
	strncpy(lp->d_packname, sc->sc_model, 16);
	lp->d_version = 1;

	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_checksum = dkcksum(lp);

	readdisklabel(DISKLABELDEV(dev), prestostrategy, lp, spoofonly);
}


code:
/*	$OpenBSD: qe.c,v 1.40 2015/06/24 09:40:53 mpi Exp $	*/

/*
 * Copyright (c) 1998, 2000 Jason L. Wright.
 * 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 AUTHORS ``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 AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Driver for the SBus qec+qe QuadEthernet board.
 *
 * This driver was written using the AMD MACE Am79C940 documentation, some
 * ideas gleaned from the S/Linux driver for this card, Solaris header files,
 * and a loan of a card from Paul Southworth of the Internet Engineering
 * Group (www.ieng.com).
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/netisr.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif

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

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

#include <sparc/dev/qecvar.h>
#include <sparc/dev/qecreg.h>
#include <sparc/dev/qereg.h>
#include <sparc/dev/qevar.h>

int	qematch(struct device *, void *, void *);
void	qeattach(struct device *, struct device *, void *);

void	qeinit(struct qesoftc *);
void	qestart(struct ifnet *);
void	qestop(struct qesoftc *);
void	qewatchdog(struct ifnet *);
int	qeioctl(struct ifnet *, u_long, caddr_t);
void	qereset(struct qesoftc *);

int		qeintr(void *);
int		qe_eint(struct qesoftc *, u_int32_t);
int		qe_rint(struct qesoftc *);
int		qe_tint(struct qesoftc *);
void		qe_read(struct qesoftc *, int, int);
void		qe_mcreset(struct qesoftc *);
void		qe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
int		qe_ifmedia_upd(struct ifnet *);

struct cfdriver qe_cd = {
	NULL, "qe", DV_IFNET
};

struct cfattach qe_ca = {
	sizeof(struct qesoftc), qematch, qeattach
};

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

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

void
qeattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct qec_softc *qec = (struct qec_softc *)parent;
	struct qesoftc *sc = (struct qesoftc *)self;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct confargs *ca = aux;
	struct bootpath *bp;
	extern void myetheraddr(u_char *);
	int pri;

	if (qec->sc_pri == 0) {
		printf(": no interrupt found on parent\n");
		return;
	}
	pri = qec->sc_pri;

	sc->sc_rev = getpropint(ca->ca_ra.ra_node, "mace-version", -1);

	sc->sc_cr = mapiodev(&ca->ca_ra.ra_reg[0], 0, sizeof(struct qe_cregs));
	sc->sc_mr = mapiodev(&ca->ca_ra.ra_reg[1], 0, sizeof(struct qe_mregs));
	sc->sc_qec = qec;
	sc->sc_qr = qec->sc_regs;
	qestop(sc);

	sc->sc_channel = getpropint(ca->ca_ra.ra_node, "channel#", -1);
	sc->sc_burst = qec->sc_burst;

	sc->sc_ih.ih_fun = qeintr;
	sc->sc_ih.ih_arg = sc;
	intr_establish(pri, &sc->sc_ih, IPL_NET, self->dv_xname);

	myetheraddr(sc->sc_arpcom.ac_enaddr);

	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
	ifp->if_softc = sc;
	ifp->if_start = qestart;
	ifp->if_ioctl = qeioctl;
	ifp->if_watchdog = qewatchdog;
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS |
	    IFF_MULTICAST;

	ifmedia_init(&sc->sc_ifmedia, IFM_IMASK,
	    qe_ifmedia_upd, qe_ifmedia_sts);
	ifmedia_add(&sc->sc_ifmedia,
	    IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0), 0, NULL);
	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T);

	IFQ_SET_MAXLEN(&ifp->if_snd, QE_TX_RING_SIZE);
	IFQ_SET_READY(&ifp->if_snd);

	/* Attach the interface. */
	if_attach(ifp);
	ether_ifattach(ifp);

	printf(" pri %d: rev %x address %s\n", pri, sc->sc_rev,
	    ether_sprintf(sc->sc_arpcom.ac_enaddr));

	bp = ca->ca_ra.ra_bp;
	if (bp != NULL && strcmp(bp->name, "qe") == 0 &&
	    sc->sc_dev.dv_unit == bp->val[1])
		bp->dev = &sc->sc_dev;
}

/*
 * Start output on interface.
 * We make two assumptions here:
 *  1) that the current priority is set to splnet _before_ this code
 *     is called *and* is returned to the appropriate priority after
 *     return
 *  2) that the IFF_OACTIVE flag is checked before this code is called
 *     (i.e. that the output part of the interface is idle)
 */
void
qestart(ifp)
	struct ifnet *ifp;
{
	struct qesoftc *sc = (struct qesoftc *)ifp->if_softc;
	struct mbuf *m;
	int bix, len;

	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
		return;

	bix = sc->sc_last_td;

	for (;;) {
		IFQ_POLL(&ifp->if_snd, m);
		if (m == NULL)
			break;

		IFQ_DEQUEUE(&ifp->if_snd, m);

#if NBPFILTER > 0
		/*
		 * If BPF is listening on this interface, let it see the
		 * packet before we commit it to the wire.
		 */
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif

		/*
		 * Copy the mbuf chain into the transmit buffer.
		 */
		len = qec_put(sc->sc_bufs->tx_buf[bix & QE_TX_RING_MASK], m);

		/*
		 * Initialize transmit registers and start transmission
		 */
		sc->sc_desc->qe_txd[bix].tx_flags =
			QE_TXD_OWN | QE_TXD_SOP | QE_TXD_EOP |
			(len & QE_TXD_LENGTH);
		sc->sc_cr->ctrl = QE_CR_CTRL_TWAKEUP;

		if (++bix == QE_TX_RING_MAXSIZE)
			bix = 0;

		if (++sc->sc_no_td == QE_TX_RING_SIZE) {
			ifp->if_flags |= IFF_OACTIVE;
			break;
		}
	}

	sc->sc_last_td = bix;
}

void
qestop(sc)
	struct qesoftc *sc;
{
	struct qe_cregs *cr = sc->sc_cr;
	struct qe_mregs *mr = sc->sc_mr;
	int n;

	mr->biucc = QE_MR_BIUCC_SWRST;
	for (n = 200; n > 0; n--) {
		if ((mr->biucc & QE_MR_BIUCC_SWRST) == 0)
			break;
		DELAY(20);
	}

	cr->ctrl = QE_CR_CTRL_RESET;
	for (n = 200; n > 0; n--) {
		if ((cr->ctrl & QE_CR_CTRL_RESET) == 0)
			break;
		DELAY(20);
	}
}

/*
 * Reset interface.
 */
void
qereset(sc)
	struct qesoftc *sc;
{
	qestop(sc);
	qeinit(sc);
}

void
qewatchdog(ifp)
	struct ifnet *ifp;
{
	struct qesoftc *sc = ifp->if_softc;
	int s;

	log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
	++sc->sc_arpcom.ac_if.if_oerrors;

	s = splnet();
	qereset(sc);
	splx(s);
}

/*
 * Interrupt dispatch.
 */
int
qeintr(v)
	void *v;
{
	struct qesoftc *sc = (struct qesoftc *)v;
	u_int32_t qecstat, qestat;
	int r = 0;

	qecstat = sc->sc_qr->stat >> (4 * sc->sc_channel);
	if ((qecstat & 0xf) == 0)
		return (r);

	qestat = sc->sc_cr->stat;

	if (qestat & QE_CR_STAT_ALLERRORS) {
		r |= qe_eint(sc, qestat);
		if (r == -1)
			return (1);
	}

	if (qestat & QE_CR_STAT_TXIRQ)
		r |= qe_tint(sc);

	if (qestat & QE_CR_STAT_RXIRQ)
		r |= qe_rint(sc);

	return (1);
}

/*
 * Transmit interrupt.
 */
int
qe_tint(sc)
	struct qesoftc *sc;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int bix;
	struct qe_txd txd;

	bix = sc->sc_first_td;

	for (;;) {
		if (sc->sc_no_td <= 0)
			break;

		txd.tx_flags = sc->sc_desc->qe_txd[bix].tx_flags;
		if (txd.tx_flags & QE_TXD_OWN)
			break;

		ifp->if_opackets++;

		if (++bix == QE_TX_RING_MAXSIZE)
			bix = 0;

		--sc->sc_no_td;
	}

	if (sc->sc_no_td == 0)
		ifp->if_timer = 0;

	/*
	 * If we freed up at least one descriptor and tx is blocked,
	 * unblock it and start it up again.
	 */
	if (sc->sc_first_td != bix) {
		sc->sc_first_td = bix;
		if (ifp->if_flags & IFF_OACTIVE) {
			ifp->if_flags &= ~IFF_OACTIVE;
			qestart(ifp);
		}
	}

	return (1);
}

/*
 * Receive interrupt.
 */
int
qe_rint(sc)
	struct qesoftc *sc;
{
	int bix, len;

	bix = sc->sc_last_rd;

	/*
	 * Process all buffers with valid data.
	 */
	for (;;) {
		if (sc->sc_desc->qe_rxd[bix].rx_flags & QE_RXD_OWN)
			break;

		len = (sc->sc_desc->qe_rxd[bix].rx_flags & QE_RXD_LENGTH) - 4;
		qe_read(sc, bix, len);
		sc->sc_desc->qe_rxd[(bix + QE_RX_RING_SIZE) & QE_RX_RING_MAXMASK].rx_flags =
		    QE_RXD_OWN | QE_RXD_LENGTH;

		if (++bix == QE_RX_RING_MAXSIZE)
			bix = 0;
	}

	sc->sc_last_rd = bix;

	return (1);
}

/*
 * Error interrupt.
 */
int
qe_eint(sc, why)
	struct qesoftc *sc;
	u_int32_t why;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int r = 0, rst = 0;

	if (why & QE_CR_STAT_EDEFER) {
		printf("%s: excessive tx defers.\n", sc->sc_dev.dv_xname);
		r |= 1;
		ifp->if_oerrors++;
	}

	if (why & QE_CR_STAT_CLOSS) {
		ifp->if_oerrors++;
		r |= 1;
	}

	if (why & QE_CR_STAT_ERETRIES) {
		printf("%s: excessive tx retries\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_LCOLL) {
		printf("%s: late tx transmission\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_FUFLOW) {
		printf("%s: tx fifo underflow\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_JERROR) {
		printf("%s: jabber seen\n", sc->sc_dev.dv_xname);
		r |= 1;
	}

	if (why & QE_CR_STAT_BERROR) {
		printf("%s: babble seen\n", sc->sc_dev.dv_xname);
		r |= 1;
	}

	if (why & QE_CR_STAT_TCCOFLOW) {
		ifp->if_collisions += 256;
		ifp->if_oerrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_TXDERROR) {
		printf("%s: tx descriptor is bad\n", sc->sc_dev.dv_xname);
		rst = 1;
		r |= 1;
	}

	if (why & QE_CR_STAT_TXLERR) {
		printf("%s: tx late error\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		rst = 1;
		r |= 1;
	}

	if (why & QE_CR_STAT_TXPERR) {
		printf("%s: tx dma parity error\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		rst = 1;
		r |= 1;
	}

	if (why & QE_CR_STAT_TXSERR) {
		printf("%s: tx dma sbus error ack\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		rst = 1;
		r |= 1;
	}

	if (why & QE_CR_STAT_RCCOFLOW) {
		ifp->if_collisions += 256;
		ifp->if_ierrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_RUOFLOW) {
		ifp->if_ierrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_MCOFLOW) {
		ifp->if_ierrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_RXFOFLOW) {
		printf("%s: rx fifo overflow\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
	}

	if (why & QE_CR_STAT_RLCOLL) {
		printf("%s: rx late collision\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		ifp->if_collisions++;
		r |= 1;
	}

	if (why & QE_CR_STAT_FCOFLOW) {
		ifp->if_ierrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_CECOFLOW) {
		ifp->if_ierrors += 256;
		r |= 1;
	}

	if (why & QE_CR_STAT_RXDROP) {
		printf("%s: rx packet dropped\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
	}

	if (why & QE_CR_STAT_RXSMALL) {
		printf("%s: rx buffer too small\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_RXLERR) {
		printf("%s: rx late error\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_RXPERR) {
		printf("%s: rx dma parity error\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
		rst = 1;
	}

	if (why & QE_CR_STAT_RXSERR) {
		printf("%s: rx dma sbus error ack\n", sc->sc_dev.dv_xname);
		ifp->if_ierrors++;
		r |= 1;
		rst = 1;
	}

	if (r == 0)
		printf("%s: unexpected interrupt error: %08x\n",
			sc->sc_dev.dv_xname, why);

	if (rst) {
		printf("%s: resetting...\n", sc->sc_dev.dv_xname);
		qereset(sc);
		return (-1);
	}

	return (r);
}

int
qeioctl(ifp, cmd, data)
	struct ifnet *ifp;
	u_long cmd;
	caddr_t data;
{
	struct qesoftc *sc = ifp->if_softc;
	struct ifaddr *ifa = (struct ifaddr *)data;
	struct ifreq *ifr = (struct ifreq *)data;
	int s, error = 0;

	s = splnet();

	switch (cmd) {
	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		switch (ifa->ifa_addr->sa_family) {
		case AF_INET:
			qeinit(sc);
			arp_ifinit(&sc->sc_arpcom, ifa);
			break;
		default:
			qeinit(sc);
			break;
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    (ifp->if_flags & IFF_RUNNING) != 0) {
			/*
			 * If interface is marked down and it is running, then
			 * stop it.
			 */
			qestop(sc);
			ifp->if_flags &= ~IFF_RUNNING;
		} else if ((ifp->if_flags & IFF_UP) != 0 &&
		    (ifp->if_flags & IFF_RUNNING) == 0) {
			/*
			 * If interface is marked up and it is stopped, then
			 * start it.
			 */
			qeinit(sc);
		} else {
			/*
			 * Reset the interface to pick up changes in any other
			 * flags that affect hardware registers.
			 */
			qestop(sc);
			qeinit(sc);
		}
		break;

	case SIOCGIFMEDIA:
	case SIOCSIFMEDIA:
		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
		break;

	default:
		error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data);
	}

	if (error == ENETRESET) {
		if (ifp->if_flags & IFF_RUNNING)
			qeinit(sc);
		error = 0;
	}

	splx(s);
	return (error);
}

void
qeinit(sc)
	struct qesoftc *sc;
{
	struct qe_mregs *mr = sc->sc_mr;
	struct qe_cregs *cr = sc->sc_cr;
	struct qec_softc *qec = sc->sc_qec;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int s = splnet();
	int i;

	qestop(sc);

	/*
	 * Allocate descriptor ring and buffers, if not already done
	 */
	if (sc->sc_desc == NULL)
		sc->sc_desc_dva = (struct qe_desc *) dvma_malloc(
		    sizeof(struct qe_desc), &sc->sc_desc, M_NOWAIT | M_ZERO);

	if (sc->sc_bufs == NULL)
		sc->sc_bufs_dva = (struct qe_bufs *) dvma_malloc(
		    sizeof(struct qe_bufs), &sc->sc_bufs, M_NOWAIT | M_ZERO);

	for (i = 0; i < QE_TX_RING_MAXSIZE; i++)
		sc->sc_desc->qe_txd[i].tx_addr =
			(u_int32_t)sc->sc_bufs_dva->tx_buf[i & QE_TX_RING_MASK];
	for (i = 0; i < QE_RX_RING_MAXSIZE; i++) {
		sc->sc_desc->qe_rxd[i].rx_addr =
			(u_int32_t)sc->sc_bufs_dva->rx_buf[i & QE_RX_RING_MASK];
		if (i < QE_RX_RING_SIZE)
			sc->sc_desc->qe_rxd[i].rx_flags =
				QE_RXD_OWN | QE_RXD_LENGTH;
	}

	cr->rxds = (u_int32_t)sc->sc_desc_dva->qe_rxd;
	cr->txds = (u_int32_t)sc->sc_desc_dva->qe_txd;

	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
	sc->sc_last_rd = 0;

	cr->rimask = 0;
	cr->timask = 0;
	cr->qmask = 0;
	cr->mmask = QE_CR_MMASK_RXCOLL | QE_CR_MMASK_CLOSS;
	cr->ccnt = 0;
	cr->pipg = 0;
	cr->rxwbufptr = cr->rxrbufptr = sc->sc_channel * qec->sc_msize;
	cr->txwbufptr = cr->txrbufptr = cr->rxrbufptr + qec->sc_rsize;

	/*
	 * When switching from mace<->qec always guarantee an sbus
	 * turnaround (if last op was read, perform a dummy write, and
	 * vice versa).
	 */
	i = cr->qmask;		/* dummy */

	mr->biucc = QE_MR_BIUCC_BSWAP | QE_MR_BIUCC_64TS;
	mr->fifofc = QE_MR_FIFOCC_TXF16 | QE_MR_FIFOCC_RXF32 |
	    QE_MR_FIFOCC_RFWU | QE_MR_FIFOCC_TFWU;
	mr->xmtfc = QE_MR_XMTFC_APADXMT;
	mr->rcvfc = 0;
	mr->imr = QE_MR_IMR_CERRM | QE_MR_IMR_RCVINTM;
	mr->phycc = QE_MR_PHYCC_ASEL;
	mr->plscc = QE_MR_PLSCC_TP;

	qe_ifmedia_upd(ifp);

	mr->iac = QE_MR_IAC_ADDRCHG | QE_MR_IAC_PHYADDR;
	for (i = 100; i > 0; i--) {
		if ((mr->iac & QE_MR_IAC_ADDRCHG) == 0)
			break;
		DELAY(2);
	}
	mr->padr = sc->sc_arpcom.ac_enaddr[0];
	mr->padr = sc->sc_arpcom.ac_enaddr[1];
	mr->padr = sc->sc_arpcom.ac_enaddr[2];
	mr->padr = sc->sc_arpcom.ac_enaddr[3];
	mr->padr = sc->sc_arpcom.ac_enaddr[4];
	mr->padr = sc->sc_arpcom.ac_enaddr[5];
	qe_mcreset(sc);
	mr->iac = 0;

	i = mr->mpc;	/* cleared on read */

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	mr->maccc = QE_MR_MACCC_ENXMT | QE_MR_MACCC_ENRCV |
	    ((ifp->if_flags & IFF_PROMISC) ? QE_MR_MACCC_PROM : 0);
	splx(s);
}

/*
 * Pass a packet to the higher levels.
 */
void
qe_read(sc, idx, len)
	struct qesoftc *sc;
	int idx, len;
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
	struct mbuf *m;

	if (len <= sizeof(struct ether_header) ||
	    len > ETHERMTU + sizeof(struct ether_header)) {

		printf("%s: invalid packet size %d; dropping\n",
			ifp->if_xname, len);

		ifp->if_ierrors++;
		return;
	}

	/*
	 * Pull packet off interface.
	 */
	m = qec_get(sc->sc_bufs->rx_buf[idx & QE_RX_RING_MASK], len);
	if (m == NULL) {
		ifp->if_ierrors++;
		return;
	}

	ml_enqueue(&ml, m);
	if_input(ifp, &ml);
}

/*
 * Reset multicast filter.
 */
void
qe_mcreset(sc)
	struct qesoftc *sc;
{
	struct arpcom *ac = &sc->sc_arpcom;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct qe_mregs *mr = sc->sc_mr;
	struct ether_multi *enm;
	struct ether_multistep step;
	u_int32_t crc;
	u_int16_t hash[4];
	u_int8_t octet, *ladrp = (u_int8_t *)&hash[0];
	int i, j;

	if (ac->ac_multirangecnt > 0)
		ifp->if_flags |= IFF_ALLMULTI;

	if (ifp->if_flags & IFF_ALLMULTI) {
		mr->iac = QE_MR_IAC_ADDRCHG | QE_MR_IAC_LOGADDR;
		for (i = 100; i > 0; i--) {
			if ((mr->iac & QE_MR_IAC_ADDRCHG) == 0)
				break;
			DELAY(2);
		}
		for (i = 0; i < 8; i++)
			mr->ladrf = 0xff;
		return;
	}

	hash[3] = hash[2] = hash[1] = hash[0] = 0;

	ETHER_FIRST_MULTI(step, ac, enm);
	while (enm != NULL) {
		crc = 0xffffffff;

		for (i = 0; i < ETHER_ADDR_LEN; i++) {
			octet = enm->enm_addrlo[i];

			for (j = 0; j < 8; j++) {
				if ((crc & 1) ^ (octet & 1)) {
					crc >>= 1;
					crc ^= MC_POLY_LE;
				}
				else
					crc >>= 1;
				octet >>= 1;
			}
		}

		crc >>= 26;
		hash[crc >> 4] |= 1 << (crc & 0xf);
		ETHER_NEXT_MULTI(step, enm);
	}

	mr->iac = QE_MR_IAC_ADDRCHG | QE_MR_IAC_LOGADDR;
	for (i = 100; i > 0; i--) {
		if ((mr->iac & QE_MR_IAC_ADDRCHG) == 0)
			break;
		DELAY(2);
	}
	for (i = 0; i < 8; i++)
		mr->ladrf = ladrp[i];
}

void
qe_ifmedia_sts(ifp, ifmr)
	struct ifnet *ifp;
	struct ifmediareq *ifmr;
{
	struct qesoftc *sc = (struct qesoftc *)ifp->if_softc;
	u_int8_t phycc;

	ifmr->ifm_active = IFM_ETHER | IFM_10_T;
	phycc = sc->sc_mr->phycc;
	if ((phycc & QE_MR_PHYCC_DLNKTST) == 0) {
		ifmr->ifm_status |= IFM_AVALID;
		if (phycc & QE_MR_PHYCC_LNKFL)
			ifmr->ifm_status &= ~IFM_ACTIVE;
		else
			ifmr->ifm_status |= IFM_ACTIVE;
	}
}

int
qe_ifmedia_upd(ifp)
	struct ifnet *ifp;
{
	struct qesoftc *sc = (struct qesoftc *)ifp->if_softc;
	int media = sc->sc_ifmedia.ifm_media;

	if (IFM_TYPE(media) != IFM_ETHER)
		return (EINVAL);

	if (IFM_SUBTYPE(media) != IFM_10_T)
		return (EINVAL);

	return (0);
}


code:
/*	$OpenBSD: qec.c,v 1.24 2015/03/30 20:30:22 miod Exp $	*/

/*
 * Copyright (c) 1998 Theo de Raadt and Jason L. Wright.
 * 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 AUTHORS ``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 AUTHORS 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/device.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/if_media.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

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

#include <sparc/dev/sbusvar.h>
#include <sparc/dev/dmareg.h>
#include <sparc/dev/qecreg.h>
#include <sparc/dev/qecvar.h>

int	qecprint(void *, const char *);
int	qecmatch(struct device *, void *, void *);
void	qecattach(struct device *, struct device *, void *);
int	qec_fix_range(struct qec_softc *, struct sbus_softc *);
void	qec_translate(struct qec_softc *, struct confargs *);

struct cfattach qec_ca = {
	sizeof(struct qec_softc), qecmatch, qecattach
};

struct cfdriver qec_cd = {
	NULL, "qec", DV_DULL
};

int
qecprint(aux, name)
	void *aux;
	const char *name;
{
	register 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);
}

/*
 * match a QEC device in a slot capable of DMA
 */
int
qecmatch(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 (!sbus_testdma((struct sbus_softc *)parent, ca))
		return (0);

	return (1);
}

/*
 * Attach all the sub-devices we can find
 */
void
qecattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	register struct confargs *ca = aux;
	struct qec_softc *sc = (void *)self;
	int node;
	struct confargs oca;
	char *name;
	int sbusburst;

	/*
	 * The first i/o space is the qec global registers, and
	 * the second is a buffer used by the qec channels internally.
	 * (It's not necessary to map the second i/o space, but knowing
	 * its size is necessary).
	 */
	sc->sc_regs = mapiodev(&ca->ca_ra.ra_reg[0], 0,
	    sizeof(struct qecregs));
	sc->sc_bufsiz = ca->ca_ra.ra_reg[1].rr_len;
	sc->sc_paddr = ca->ca_ra.ra_reg[0].rr_paddr;

	/*
	 * On qec+qe, the qec has the interrupt priority, but we
	 * need to pass that down so that the qe's can handle them.
	 */
	if (ca->ca_ra.ra_nintr == 1)
		sc->sc_pri = ca->ca_ra.ra_intr[0].int_pri;

	node = sc->sc_node = ca->ca_ra.ra_node;

	if (qec_fix_range(sc, (struct sbus_softc *)parent) != 0)
		return;

	/*
	 * Get transfer burst size from PROM
	 */
	sbusburst = ((struct sbus_softc *)parent)->sc_burst;
	if (sbusburst == 0)
		sbusburst = SBUS_BURST_32 - 1; /* 1->16 */

	sc->sc_nchannels = getpropint(ca->ca_ra.ra_node, "#channels", -1);
	if (sc->sc_nchannels == -1) {
		printf(": no channels\n");
		return;
	}
	else if (sc->sc_nchannels < 1 || sc->sc_nchannels > 4) {
		printf(": invalid number of channels: %d\n", sc->sc_nchannels);
		return;
	}

	sc->sc_burst = getpropint(ca->ca_ra.ra_node, "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(": %dK memory %d channel%s",
	    sc->sc_bufsiz / 1024, sc->sc_nchannels,
	    (sc->sc_nchannels == 1) ? "" : "s");

	node = sc->sc_node = ca->ca_ra.ra_node;

	/* Propagate bootpath */
	if (ca->ca_ra.ra_bp != NULL)
		oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
	else
		oca.ca_ra.ra_bp = NULL;

	printf("\n");

	qec_reset(sc);

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

		qec_translate(sc, &oca);
		oca.ca_bustype = BUS_SBUS;
		oca.ca_dmat = ca->ca_dmat;
		config_found(&sc->sc_dev, (void *)&oca, qecprint);
	}
}

int
qec_fix_range(sc, sbp)
	struct qec_softc *sc;
	struct sbus_softc *sbp;
{
	int rlen, i, j;

	rlen = getproplen(sc->sc_node, "ranges");
	sc->sc_range =
		(struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
	if (sc->sc_range == NULL) {
		printf(": PROM ranges too large: %d\n", rlen);
		return EINVAL;
	}
	sc->sc_nrange = rlen / sizeof(struct rom_range);
	(void)getprop(sc->sc_node, "ranges", sc->sc_range, rlen);

	if (sbp->sc_nrange == 0) {
		/*
		 * Old-style SBus configuration: we need to compute the
		 * physical address of the board's base, in order to be
		 * able to compute proper physical addresses in
		 * qec_translate() below.
		 */
		struct rom_reg rr[RA_MAXREG];
		int reglen;

		reglen = getprop(sc->sc_node, "reg", rr, sizeof rr);
		/* shouldn't happen */
		if (reglen <= 0 || reglen % sizeof(struct rom_reg) != 0) {
			printf(": unexpected \"reg\" property layout\n");
			return EINVAL;
		}
		for (i = 0; i < sc->sc_nrange; i++) {
			sc->sc_range[i].poffset +=
			    sc->sc_paddr - rr[0].rr_paddr;
		}
	} else {
		for (i = 0; i < sc->sc_nrange; i++) {
			for (j = 0; j < sbp->sc_nrange; j++) {
				if (sc->sc_range[i].pspace ==
				    sbp->sc_range[j].cspace) {
					sc->sc_range[i].poffset +=
					    sbp->sc_range[j].poffset;
					sc->sc_range[i].pspace =
					    sbp->sc_range[j].pspace;
					break;
				}
			}
		}
	}

	return 0;
}

/*
 * Translate the register addresses of our children
 */
void
qec_translate(sc, ca)
	struct qec_softc *sc;
	struct confargs *ca;
{
	register int i;

	ca->ca_slot = ca->ca_ra.ra_iospace;
	ca->ca_offset = sc->sc_range[ca->ca_slot].poffset - (long)sc->sc_paddr;

	/* Translate into parent address spaces */
	for (i = 0; i < ca->ca_ra.ra_nreg; i++) {
		int j, cspace = ca->ca_ra.ra_reg[i].rr_iospace;

		for (j = 0; j < sc->sc_nrange; j++) {
			if (sc->sc_range[j].cspace == cspace) {
				ca->ca_ra.ra_reg[i].rr_paddr +=
					sc->sc_range[j].poffset;
				ca->ca_ra.ra_reg[i].rr_iospace =
					sc->sc_range[j].pspace;
				break;
			}
		}
	}
}

/*
 * Reset the QEC and initialize its global registers.
 */
void
qec_reset(sc)
	struct qec_softc *sc;
{
	struct qecregs *qr = sc->sc_regs;
	int i = 200;

	qr->ctrl = QEC_CTRL_RESET;
	while (--i) {
		if ((qr->ctrl & QEC_CTRL_RESET) == 0)
			break;
		DELAY(20);
	}
	if (i == 0) {
		printf("%s: reset failed.\n", sc->sc_dev.dv_xname);
		return;
	}

	qr->msize = sc->sc_bufsiz / sc->sc_nchannels;
	sc->sc_msize = qr->msize;

	qr->rsize = sc->sc_bufsiz / (sc->sc_nchannels * 2);
	sc->sc_rsize = qr->rsize;

	qr->tsize = sc->sc_bufsiz / (sc->sc_nchannels * 2);

	qr->psize = QEC_PSIZE_2048;

        if (sc->sc_burst & SBUS_BURST_64)
		i = QEC_CTRL_B64;
	else if (sc->sc_burst & SBUS_BURST_32)
		i = QEC_CTRL_B32;
	else
		i = QEC_CTRL_B16;

	qr->ctrl = (qr->ctrl & QEC_CTRL_MODEMASK) | i;
}

/*
 * Routine to copy from mbuf chain to transmit buffer in
 * network buffer memory.
 */
int
qec_put(buf, m0)
	u_int8_t *buf;
	struct mbuf *m0;
{
	struct mbuf *m;
	int len, tlen = 0;

	for (m = m0; m != NULL; m = m->m_next) {
		len = m->m_len;
		bcopy(mtod(m, caddr_t), buf, len);
		buf += len;
		tlen += len;
	}
	m_freem(m0);
	return (tlen);
}

/*
 * Pull data off an interface.
 * Len is the length of data, with local net header stripped.
 * We copy the data into mbufs.  When full cluster sized units are present,
 * we copy into clusters.
 */
struct mbuf *
qec_get(buf, totlen)
	u_int8_t *buf;
	int totlen;
{
	struct mbuf *m, *top, **mp;
	int len, pad;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return (NULL);
	m->m_pkthdr.len = totlen;
	pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);
	len = MHLEN;
	if (totlen >= MINCLSIZE) {
		MCLGET(m, M_DONTWAIT);
		if (m->m_flags & M_EXT)
			len = MCLBYTES;
	}
	m->m_data += pad;
	len -= pad;
	top = NULL;
	mp = ⊤

	while (totlen > 0) {
		if (top) {
			MGET(m, M_DONTWAIT, MT_DATA);
			if (m == NULL) {
				m_freem(top);
				return NULL;
			}
			len = MLEN;
		}
		if (top && totlen >= MINCLSIZE) {
			MCLGET(m, M_DONTWAIT);
			if (m->m_flags & M_EXT)
				len = MCLBYTES;
		}
		m->m_len = len = min(totlen, len);
		bcopy(buf, mtod(m, caddr_t), len);
		buf += len;
		totlen -= len;
		*mp = m;
		mp = &m->m_next;
	}

	return (top);
}