code:
/*	$OpenBSD: gpx.c,v 1.25 2014/12/23 21:39:12 miod Exp $	*/
/*
 * Copyright (c) 2006 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice, this permission notice, and the disclaimer below
 * appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*-
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. 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.
 *
 *	@(#)qd.c	7.1 (Berkeley) 6/28/91
 */

/************************************************************************
*									*
*			Copyright (c) 1985-1988 by			*
*		Digital Equipment Corporation, Maynard, MA		*
*			All rights reserved.				*
*									*
*   This software is furnished under a license and may be used and	*
*   copied  only  in accordance with the terms of such license and	*
*   with the  inclusion  of  the  above  copyright  notice.   This	*
*   software  or  any  other copies thereof may not be provided or	*
*   otherwise made available to any other person.  No title to and	*
*   ownership of the software is hereby transferred.			*
*									*
*   The information in this software is subject to change  without	*
*   notice  and should not be construed as a commitment by Digital	*
*   Equipment Corporation.						*
*									*
*   Digital assumes no responsibility for the use  or  reliability	*
*   of its software on equipment which is not supplied by Digital.	*
*									*
*************************************************************************/

/*
 * Driver for the GPX color option on VAXstation 3100, based on the
 * MicroVAX II qdss driver.
 *
 * The frame buffer memory itself is not directly accessible (unlike
 * the on-board monochrome smg frame buffer), and writes through the
 * Dragon chip can only happen in multiples of 16 pixels, horizontally.
 *
 * Because of this limitation, the font image is copied to offscreen
 * memory (which there is plenty of), and screen to screen blt operations
 * are done for everything.
 */

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

#include <machine/sid.h>
#include <machine/cpu.h>
#include <machine/ka420.h>
#include <machine/scb.h>
#include <machine/vsbus.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

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

#include <dev/ic/bt458reg.h>
#if 0
#include <dev/ic/dc503reg.h>
#endif
#include <vax/qbus/qdreg.h>

#define	GPXADDR		0x3c000000	/* base address on VAXstation 3100 */

#define	GPX_ADDER_OFFSET	0x0000
#define	GPX_VDAC_OFFSET		0x0300
#define	GPX_CURSOR_OFFSET	0x0400	/* DC503 */
#define	GPX_READBACK_OFFSET	0x0500

#define	GPX_WIDTH	1024
#define	GPX_VISHEIGHT	864
#define	GPX_HEIGHT	2048

/* 4 plane option RAMDAC */
struct	ramdac4 {
	u_int16_t	colormap[16];
	u_int8_t	unknown[0x20];
	u_int16_t	cursormap[4];
	u_int8_t	unknown2[0x18];
	u_int16_t	control;
#define	RAMDAC4_INIT	0x0047
#define	RAMDAC4_ENABLE	0x0002
};

/* 8 plane option RAMDAC - Bt458 or compatible */
struct	ramdac8 {
	u_int16_t	address;
	u_int16_t	cmapdata;
	u_int16_t	control;
	u_int16_t	omapdata;
};

int	gpx_match(struct device *, void *, void *);
void	gpx_attach(struct device *, struct device *, void *);

struct	gpx_screen {
	struct rasops_info ss_ri;
	int		ss_console;
	u_int		ss_depth;
	u_int		ss_gpr;		/* font glyphs per row */
	struct adder	*ss_adder;
	void 		*ss_vdac;
	u_int8_t	ss_cmap[256 * 3];
#if 0
	struct dc503reg	*ss_cursor;
	u_int16_t	ss_curcmd;
#endif
};

/* for console */
struct gpx_screen gpx_consscr;

struct	gpx_softc {
	struct device sc_dev;
	struct gpx_screen *sc_scr;
	int	sc_nscreens;
};

struct cfattach gpx_ca = {
	sizeof(struct gpx_softc), gpx_match, gpx_attach,
};

struct	cfdriver gpx_cd = {
	NULL, "gpx", DV_DULL
};

struct wsscreen_descr gpx_stdscreen = {
	"std",
};

const struct wsscreen_descr *_gpx_scrlist[] = {
	&gpx_stdscreen,
};

const struct wsscreen_list gpx_screenlist = {
	sizeof(_gpx_scrlist) / sizeof(struct wsscreen_descr *),
	_gpx_scrlist,
};

int	gpx_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	gpx_mmap(void *, off_t, int);
int	gpx_alloc_screen(void *, const struct wsscreen_descr *,
	    void **, int *, int *, long *);
void	gpx_free_screen(void *, void *);
int	gpx_show_screen(void *, void *, int,
	    void (*) (void *, int, int), void *);
int	gpx_load_font(void *, void *, struct wsdisplay_font *);
int	gpx_list_font(void *, struct wsdisplay_font *);
void	gpx_burner(void *, u_int, u_int);

const struct wsdisplay_accessops gpx_accessops = {
	.ioctl = gpx_ioctl,
	.mmap = gpx_mmap,
	.alloc_screen = gpx_alloc_screen,
	.free_screen = gpx_free_screen,
	.show_screen = gpx_show_screen,
	.load_font = gpx_load_font,
	.list_font = gpx_list_font,
	.burn_screen = gpx_burner
};

void	gpx_clear_screen(struct gpx_screen *);
void	gpx_copyrect(struct gpx_screen *, int, int, int, int, int, int);
void	gpx_fillrect(struct gpx_screen *, int, int, int, int, long, u_int);
int	gpx_getcmap(struct gpx_screen *, struct wsdisplay_cmap *);
void	gpx_loadcmap(struct gpx_screen *, int, int);
int	gpx_putcmap(struct gpx_screen *, struct wsdisplay_cmap *);
void	gpx_resetcmap(struct gpx_screen *);
void	gpx_reset_viper(struct gpx_screen *);
int	gpx_setup_screen(struct gpx_screen *);
void	gpx_upload_font(struct gpx_screen *);
int	gpx_viper_write(struct gpx_screen *, u_int, u_int16_t);
int	gpx_wait(struct gpx_screen *, int);

int	gpx_copycols(void *, int, int, int, int);
int	gpx_copyrows(void *, int, int, int);
int	gpx_do_cursor(struct rasops_info *);
int	gpx_erasecols(void *, int, int, int, long);
int	gpx_eraserows(void *, int, int, long);
int	gpx_putchar(void *, int, int, u_int, long);

/*
 * Autoconf glue
 */

int
gpx_match(struct device *parent, void *vcf, void *aux)
{
	struct vsbus_attach_args *va = aux;
	volatile struct adder *adder;
	vaddr_t tmp;
	u_int depth;
	u_short status;
	extern struct consdev wsdisplay_cons;
	extern int oldvsbus;

	switch (vax_boardtype) {
	default:
		return (0);

	case VAX_BTYP_410:
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		if (va->va_paddr != GPXADDR)
			return (0);

		/* not present on microvaxes */
		if ((vax_confdata & KA420_CFG_MULTU) != 0)
			return (0);

		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
			return (0);
		break;
	}

	/* Check for hardware */
	adder = (volatile struct adder *)
	    vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1);
	if (adder == NULL)
		return (0);
	adder->status = 0;
	status = adder->status;
	vax_unmap_physmem((vaddr_t)adder, 1);
	if (status == offsetof(struct adder, status))
		return (0);

	/* Check for a recognized color depth */
	tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
	if (tmp == 0L)
		return (0);
	depth = (*(u_int16_t *)tmp) & 0x00f0;
	vax_unmap_physmem(tmp, 1);
	if (depth != 0x00f0 && depth != 0x0080)
		return (0);

	/* when already running as console, always fake things */
	if ((vax_confdata & KA420_CFG_L3CON) == 0 &&
	    cn_tab == &wsdisplay_cons) {
		struct vsbus_softc *sc = (void *)parent;
		sc->sc_mask = 0x08;
		scb_fake(0x44, oldvsbus ? 0x14 : 0x15);
	} else {
		adder = (struct adder *)vax_map_physmem(va->va_paddr +
		    GPX_ADDER_OFFSET, 1);
		if (adder == NULL)
			return (0);
		adder->interrupt_enable = FRAME_SYNC;
		DELAY(100000);	/* enough to get a retrace interrupt */
		adder->interrupt_enable = 0;
		vax_unmap_physmem((vaddr_t)adder, 1);
	}
	return (20);
}

void
gpx_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpx_softc *sc = (struct gpx_softc *)self;
	struct vsbus_attach_args *va = aux;
	struct gpx_screen *scr;
	struct wsemuldisplaydev_attach_args aa;
	int console;
	vaddr_t tmp;
	extern struct consdev wsdisplay_cons;

	console = (vax_confdata & KA420_CFG_L3CON) == 0 &&
	    cn_tab == &wsdisplay_cons;
	if (console) {
		scr = &gpx_consscr;
		sc->sc_nscreens = 1;
	} else {
		scr = malloc(sizeof(struct gpx_screen), M_DEVBUF, M_NOWAIT);
		if (scr == NULL) {
			printf(": can not allocate memory\n");
			return;
		}

		tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
		if (tmp == 0L) {
			printf(": can not probe depth\n");
			goto bad1;
		}
		scr->ss_depth = (*(u_int16_t *)tmp & 0x00f0) == 0x00f0 ? 4 : 8;
		vax_unmap_physmem(tmp, 1);

		scr->ss_adder = (struct adder *)vax_map_physmem(va->va_paddr +
		    GPX_ADDER_OFFSET, 1);
		if (scr->ss_adder == NULL) {
			printf(": can not map frame buffer registers\n");
			goto bad1;
		}

		scr->ss_vdac = (void *)vax_map_physmem(va->va_paddr +
		    GPX_VDAC_OFFSET, 1);
		if (scr->ss_vdac == NULL) {
			printf(": can not map RAMDAC\n");
			goto bad2;
		}

#if 0
		scr->ss_cursor =
		    (struct dc503reg *)vax_map_physmem(va->va_paddr +
		    GPX_CURSOR_OFFSET, 1);
		if (scr->ss_cursor == NULL) {
			printf(": can not map cursor chip\n");
			goto bad3;
		}
#endif

		if (gpx_setup_screen(scr) != 0) {
			printf(": initialization failed\n");
			goto bad4;
		}
	}
	sc->sc_scr = scr;

	printf("\n%s: %dx%d %d plane color framebuffer\n",
	    self->dv_xname, GPX_WIDTH, GPX_VISHEIGHT, scr->ss_depth);

	aa.console = console;
	aa.scrdata = &gpx_screenlist;
	aa.accessops = &gpx_accessops;
	aa.accesscookie = sc;
	aa.defaultscreens = 0;

	config_found(self, &aa, wsemuldisplaydevprint);

	return;

bad4:
#if 0
	vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1);
bad3:
#endif
	vax_unmap_physmem((vaddr_t)scr->ss_vdac, 1);
bad2:
	vax_unmap_physmem((vaddr_t)scr->ss_adder, 1);
bad1:
	free(scr, M_DEVBUF, sizeof(struct gpx_screen));
}

/*
 * wsdisplay accessops
 */

int
gpx_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct gpx_softc *sc = v;
	struct gpx_screen *ss = sc->sc_scr;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_cmap *cm;
	int error;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_GPX;
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = ss->ss_ri.ri_height;
		wdf->width = ss->ss_ri.ri_width;
		wdf->depth = ss->ss_depth;
		wdf->cmsize = 1 << ss->ss_depth;
		break;

	case WSDISPLAYIO_GETCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = gpx_getcmap(ss, cm);
		if (error != 0)
			return (error);
		break;
	case WSDISPLAYIO_PUTCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = gpx_putcmap(ss, cm);
		if (error != 0)
			return (error);
		gpx_loadcmap(ss, cm->index, cm->count);
		break;

	case WSDISPLAYIO_GVIDEO:
	case WSDISPLAYIO_SVIDEO:
		break;

	case WSDISPLAYIO_LINEBYTES:	/* no linear mapping */
	default:
		return (-1);
	}

	return (0);
}

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

int
gpx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
	struct gpx_softc *sc = v;
	struct gpx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

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

	*cookiep = ri;
	*curxp = *curyp = 0;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, defattrp);
	sc->sc_nscreens++;

	return (0);
}

void
gpx_free_screen(void *v, void *cookie)
{
	struct gpx_softc *sc = v;

	sc->sc_nscreens--;
}

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

int
gpx_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
{
	struct gpx_softc *sc = v;
	struct gpx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;
	int wsfcookie;
	struct wsdisplay_font *wsf;
	const char *name;

	/*
	 * We can't use rasops_load_font() directly, as we need to make
	 * sure that, when switching fonts, the font bits are set up in
	 * the correct bit order, and uploaded off-screen.
	 */

	if (font->data != NULL)
		return rasops_load_font(ri, emulcookie, font);

	/* allow an empty font name to revert to the initial font choice */
	name = font->name;
	if (*name == '\0')
		name = NULL;

	wsfcookie = wsfont_find(name, ri->ri_font->fontwidth,
	    ri->ri_font->fontheight, 0);
	if (wsfcookie < 0) {
		wsfcookie = wsfont_find(name, 0, 0, 0);
		if (wsfcookie < 0)
			return ENOENT;
		else
			return EINVAL;
	}
	if (wsfont_lock(wsfcookie, &wsf,
	    WSDISPLAY_FONTORDER_R2L, WSDISPLAY_FONTORDER_L2R) <= 0)
		return EINVAL;

	/* if (ri->ri_wsfcookie >= 0) */
		wsfont_unlock(ri->ri_wsfcookie);
	ri->ri_wsfcookie = wsfcookie;
	ri->ri_font = wsf;
	ri->ri_fontscale = ri->ri_font->fontheight * ri->ri_font->stride;

	gpx_upload_font(ss);

	return 0;
}

int
gpx_list_font(void *v, struct wsdisplay_font *font)
{
	struct gpx_softc *sc = v;
	struct gpx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_list_font(ri, font);
}

void
gpx_burner(void *v, u_int on, u_int flags)
{
	struct gpx_softc *sc = v;
	struct gpx_screen *ss = sc->sc_scr;

	if (ss->ss_depth == 8) {
		struct ramdac8 *rd = ss->ss_vdac;
		rd->address = BT_CR;
		if (on)
			rd->control = BTCR_RAMENA | BTCR_BLINK_1648 |
			    BTCR_MPLX_4;
		else
			/* fade colormap to black as well? */
			rd->control = BTCR_BLINK_1648 | BTCR_MPLX_4;
	} else {
		struct ramdac4 *rd = ss->ss_vdac;
		if (on)
			rd->control = RAMDAC4_INIT;
		else
			rd->control = RAMDAC4_INIT & ~RAMDAC4_ENABLE;
	}
}

/*
 * wsdisplay emulops
 */

int
gpx_putchar(void *v, int row, int col, u_int uc, long attr)
{
	struct rasops_info *ri = v;
	struct gpx_screen *ss = ri->ri_hw;
	struct wsdisplay_font *font = ri->ri_font;
	int dx, dy, sx, sy, fg, bg, ul;

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

	/* find where to output the glyph... */
	dx = col * font->fontwidth + ri->ri_xorigin;
	dy = row * font->fontheight + ri->ri_yorigin;
	/* ... and where to pick it from */
	uc -= font->firstchar;
	sx = (uc % ss->ss_gpr) * font->stride * NBBY;
	sy = GPX_HEIGHT - (1 + uc / ss->ss_gpr) * font->fontheight;

	/* setup VIPER operand control registers */
	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff));
	gpx_viper_write(ss, SRC1_OCR_B,
	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
	gpx_viper_write(ss, DST_OCR_B,
	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
	gpx_viper_write(ss, MASK_1, 0xffff);
	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
	ss->ss_adder->x_clip_min = 0;
	ss->ss_adder->x_clip_max = GPX_WIDTH;
	ss->ss_adder->y_clip_min = 0;
	ss->ss_adder->y_clip_max = GPX_VISHEIGHT;
	/* load DESTINATION origin and vectors */
	ss->ss_adder->fast_dest_dy = 0;
	ss->ss_adder->slow_dest_dx = 0;
	ss->ss_adder->error_1 = 0;
	ss->ss_adder->error_2 = 0;
	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
	gpx_wait(ss, RASTEROP_COMPLETE);
	ss->ss_adder->destination_x = dx;
	ss->ss_adder->fast_dest_dx = font->fontwidth;
	ss->ss_adder->destination_y = dy;
	ss->ss_adder->slow_dest_dy = font->fontheight;
	/* load SOURCE origin and vectors */
	ss->ss_adder->source_1_x = sx;
	ss->ss_adder->source_1_y = sy;
	ss->ss_adder->source_1_dx = font->fontwidth;
	ss->ss_adder->source_1_dy = font->fontheight;
	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;

	if (ul != 0) {
		gpx_fillrect(ss, dx, dy + font->fontheight - 2, font->fontwidth,
		    1, attr, LF_R3);	/* fg fill */
	}

	return 0;
}

int
gpx_copycols(void *v, int row, int src, int dst, int cnt)
{
	struct rasops_info *ri = v;
	struct gpx_screen *ss = ri->ri_hw;
	struct wsdisplay_font *font = ri->ri_font;
	int sx, y, dx, w, h;

	sx = ri->ri_xorigin + src * font->fontwidth;
	dx = ri->ri_xorigin + dst * font->fontwidth;
	w = cnt * font->fontwidth;
	y = ri->ri_yorigin + row * font->fontheight;
	h = font->fontheight;

	gpx_copyrect(ss, sx, y, dx, y, w, h);

	return 0;
}

int
gpx_erasecols(void *v, int row, int col, int cnt, long attr)
{
	struct rasops_info *ri = v;
	struct gpx_screen *ss = ri->ri_hw;
	struct wsdisplay_font *font = ri->ri_font;
	int x, y, dx, dy;

	x = ri->ri_xorigin + col * font->fontwidth;
	dx = cnt * font->fontwidth;
	y = ri->ri_yorigin + row * font->fontheight;
	dy = font->fontheight;

	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */

	return 0;
}

int
gpx_copyrows(void *v, int src, int dst, int cnt)
{
	struct rasops_info *ri = v;
	struct gpx_screen *ss = ri->ri_hw;
	struct wsdisplay_font *font = ri->ri_font;
	int x, sy, dy, w, h;

	x = ri->ri_xorigin;
	w = ri->ri_emustride;
	sy = ri->ri_yorigin + src * font->fontheight;
	dy = ri->ri_yorigin + dst * font->fontheight;
	h = cnt * font->fontheight;

	gpx_copyrect(ss, x, sy, x, dy, w, h);

	return 0;
}

int
gpx_eraserows(void *v, int row, int cnt, long attr)
{
	struct rasops_info *ri = v;
	struct gpx_screen *ss = ri->ri_hw;
	struct wsdisplay_font *font = ri->ri_font;
	int x, y, dx, dy;

	x = ri->ri_xorigin;
	dx = ri->ri_emustride;
	y = ri->ri_yorigin + row * font->fontheight;
	dy = cnt * font->fontheight;

	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */

	return 0;
}

int
gpx_do_cursor(struct rasops_info *ri)
{
	struct gpx_screen *ss = ri->ri_hw;
	int x, y, w, h;

	x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
	y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
	w = ri->ri_font->fontwidth;
	h = ri->ri_font->fontheight;

	gpx_fillrect(ss, x, y, w, h, WSCOL_WHITE << 24, LF_R4);	/* invert */

	return 0;
}

/*
 * low-level programming routines
 */

int
gpx_wait(struct gpx_screen *ss, int bits)
{
	int i;

	ss->ss_adder->status = 0;
	for (i = 100000; i != 0; i--) {
		if ((ss->ss_adder->status & bits) == bits)
			break;
		DELAY(1);
	}

	return (i == 0);
}

int
gpx_viper_write(struct gpx_screen *ss, u_int reg, u_int16_t val)
{
	if (gpx_wait(ss, ADDRESS_COMPLETE) == 0 &&
	    gpx_wait(ss, TX_READY) == 0) {
		ss->ss_adder->id_data = val;
		ss->ss_adder->command = ID_LOAD | reg;
		return (0);
	}
#ifdef DEBUG
	if (ss->ss_console == 0)	/* don't make things worse! */
		printf("gpx_viper_write failure, reg %x val %x\n", reg, val);
#endif
	return (1);
}

/* Initialize the damned beast. Straight from qdss. */
void
gpx_reset_viper(struct gpx_screen *ss)
{
	int i;

	ss->ss_adder->interrupt_enable = 0;
	ss->ss_adder->command = CANCEL;
	/* set monitor timing */
	ss->ss_adder->x_scan_count_0 = 0x2800;
	ss->ss_adder->x_scan_count_1 = 0x1020;
	ss->ss_adder->x_scan_count_2 = 0x003a;
	ss->ss_adder->x_scan_count_3 = 0x38f0;
	ss->ss_adder->x_scan_count_4 = 0x6128;
	ss->ss_adder->x_scan_count_5 = 0x093a;
	ss->ss_adder->x_scan_count_6 = 0x313c;
	ss->ss_adder->sync_phase_adj = 0x0100;
	ss->ss_adder->x_scan_conf = 0x00c8;
	/*
	 * got a bug in second pass ADDER! lets take care of it...
	 *
	 * normally, just use the code in the following bug fix code, but to
	 * make repeated demos look pretty, load the registers as if there was
	 * no bug and then test to see if we are getting sync
	 */
	ss->ss_adder->y_scan_count_0 = 0x135f;
	ss->ss_adder->y_scan_count_1 = 0x3363;
	ss->ss_adder->y_scan_count_2 = 0x2366;
	ss->ss_adder->y_scan_count_3 = 0x0388;
	/*
	 * if no sync, do the bug fix code
	 */
	if (gpx_wait(ss, FRAME_SYNC) != 0) {
		/*
		 * First load all Y scan registers with very short frame and
		 * wait for scroll service.  This guarantees at least one SYNC
		 * to fix the pass 2 Adder initialization bug (synchronizes
		 * XCINCH with DMSEEDH)
		 */
		ss->ss_adder->y_scan_count_0 = 0x01;
		ss->ss_adder->y_scan_count_1 = 0x01;
		ss->ss_adder->y_scan_count_2 = 0x01;
		ss->ss_adder->y_scan_count_3 = 0x01;
		/* delay at least 1 full frame time */
		gpx_wait(ss, FRAME_SYNC);
		gpx_wait(ss, FRAME_SYNC);
		/*
		 * now load the REAL sync values (in reverse order just to
		 * be safe).
		 */
		ss->ss_adder->y_scan_count_3 = 0x0388;
		ss->ss_adder->y_scan_count_2 = 0x2366;
		ss->ss_adder->y_scan_count_1 = 0x3363;
		ss->ss_adder->y_scan_count_0 = 0x135f;
	}
	/* zero the index registers */
	ss->ss_adder->x_index_pending = 0;
	ss->ss_adder->y_index_pending = 0;
	ss->ss_adder->x_index_new = 0;
	ss->ss_adder->y_index_new = 0;
	ss->ss_adder->x_index_old = 0;
	ss->ss_adder->y_index_old = 0;
	ss->ss_adder->pause = 0;
	/* set rasterop mode to normal pen down */
	ss->ss_adder->rasterop_mode =
	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
	/* set the rasterop registers to default values */
	ss->ss_adder->source_1_dx = 1;
	ss->ss_adder->source_1_dy = 1;
	ss->ss_adder->source_1_x = 0;
	ss->ss_adder->source_1_y = 0;
	ss->ss_adder->destination_x = 0;
	ss->ss_adder->destination_y = 0;
	ss->ss_adder->fast_dest_dx = 1;
	ss->ss_adder->fast_dest_dy = 0;
	ss->ss_adder->slow_dest_dx = 0;
	ss->ss_adder->slow_dest_dy = 1;
	ss->ss_adder->error_1 = 0;
	ss->ss_adder->error_2 = 0;
	/* scale factor = UNITY */
	ss->ss_adder->fast_scale = UNITY;
	ss->ss_adder->slow_scale = UNITY;
	/* set the source 2 parameters */
	ss->ss_adder->source_2_x = 0;
	ss->ss_adder->source_2_y = 0;
	ss->ss_adder->source_2_size = 0x0022;
	/* initialize plane addresses for eight vipers */
	for (i = 0; i < 8; i++) {
		gpx_viper_write(ss, CS_UPDATE_MASK, 1 << i);
		gpx_viper_write(ss, PLANE_ADDRESS, i);
	}
	/* initialize the external registers. */
	gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
	gpx_viper_write(ss, CS_SCROLL_MASK, 0x00ff);
	/* initialize resolution mode */
	gpx_viper_write(ss, MEMORY_BUS_WIDTH, 0x000c);	/* bus width = 16 */
	gpx_viper_write(ss, RESOLUTION_MODE, 0x0000);	/* one bit/pixel */
	/* initialize viper registers */
	gpx_viper_write(ss, SCROLL_CONSTANT,
	    SCROLL_ENABLE | VIPER_LEFT | VIPER_UP);
	gpx_viper_write(ss, SCROLL_FILL, 0x0000);
	/* set clipping and scrolling limits to full screen */
	gpx_wait(ss, ADDRESS_COMPLETE);
	ss->ss_adder->x_clip_min = 0;
	ss->ss_adder->x_clip_max = GPX_WIDTH;
	ss->ss_adder->y_clip_min = 0;
	ss->ss_adder->y_clip_max = GPX_HEIGHT;
	ss->ss_adder->scroll_x_min = 0;
	ss->ss_adder->scroll_x_max = GPX_WIDTH;
	ss->ss_adder->scroll_y_min = 0;
	ss->ss_adder->scroll_y_max = GPX_HEIGHT;
	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->x_index_pending = 0;
	ss->ss_adder->y_index_pending = 0;
	ss->ss_adder->x_index_new = 0;
	ss->ss_adder->y_index_new = 0;
	ss->ss_adder->x_index_old = 0;
	ss->ss_adder->y_index_old = 0;
	gpx_wait(ss, ADDRESS_COMPLETE);
	gpx_viper_write(ss, LEFT_SCROLL_MASK, 0x0000);
	gpx_viper_write(ss, RIGHT_SCROLL_MASK, 0x0000);
	/* set source and the mask register to all ones */
	gpx_viper_write(ss, SOURCE, 0xffff);
	gpx_viper_write(ss, MASK_1, 0xffff);
	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
	/* initialize Operand Control Register banks for fill command */
	gpx_viper_write(ss, SRC1_OCR_A, EXT_NONE | INT_M1_M2  | NO_ID | WAIT);
	gpx_viper_write(ss, SRC2_OCR_A, EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
	gpx_viper_write(ss, DST_OCR_A, EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
	gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_SOURCE | NO_ID | WAIT);
	gpx_viper_write(ss, SRC2_OCR_B, EXT_NONE | INT_M1_M2  | NO_ID | NO_WAIT);
	gpx_viper_write(ss, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_WAIT);

	/*
	 * Init Logic Unit Function registers.
	 */
	/* putchar */
	gpx_viper_write(ss, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
	/* erase{cols,rows} */
	gpx_viper_write(ss, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_ZEROS);
	/* underline */
	gpx_viper_write(ss, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_ONES);
	/* cursor */
	gpx_viper_write(ss, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_NOT_D);
}

/* Clear the whole screen. Straight from qdss. */
void
gpx_clear_screen(struct gpx_screen *ss)
{
	ss->ss_adder->x_limit = GPX_WIDTH;
	ss->ss_adder->y_limit = GPX_HEIGHT;
	ss->ss_adder->y_offset_pending = 0;
	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_offset_pending = GPX_VISHEIGHT;
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_offset_pending = 2 * GPX_VISHEIGHT;
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->y_offset_pending = 0;	 /* back to normal */
	gpx_wait(ss, FRAME_SYNC);
	gpx_wait(ss, FRAME_SYNC);
	ss->ss_adder->x_limit = GPX_WIDTH;
	ss->ss_adder->y_limit = GPX_VISHEIGHT;
}

int
gpx_setup_screen(struct gpx_screen *ss)
{
	struct rasops_info *ri = &ss->ss_ri;
	int cookie;

	bzero(ri, sizeof(*ri));
	ri->ri_depth = 8;	/* masquerade as a 8 bit device for rasops */
	ri->ri_width = GPX_WIDTH;
	ri->ri_height = GPX_VISHEIGHT;
	ri->ri_stride = GPX_WIDTH;
	ri->ri_flg = RI_CENTER;		/* no RI_CLEAR as ri_bits is NULL! */
	ri->ri_hw = ss;

	/*
	 * We can not let rasops select our font, because we need to use
	 * a font with right-to-left bit order on this frame buffer.
	 */
	wsfont_init();
	cookie = wsfont_find(NULL, 12, 0, 0);
	if (cookie <= 0)
		cookie = wsfont_find(NULL, 8, 0, 0);
	if (cookie <= 0)
		cookie = wsfont_find(NULL, 0, 0, 0);
	if (cookie <= 0)
		return (-1);
	if (wsfont_lock(cookie, &ri->ri_font,
	    WSDISPLAY_FONTORDER_R2L, WSDISPLAY_FONTORDER_L2R) <= 0)
		return (-1);
	ri->ri_wsfcookie = cookie;

	/*
	 * Ask for an unholy big display, rasops will trim this to more
	 * reasonable values.
	 */
	if (rasops_init(ri, 160, 160) != 0)
		return (-1);

	/*
	 * Override the rasops emulops.
	 */
	ri->ri_ops.copyrows = gpx_copyrows;
	ri->ri_ops.copycols = gpx_copycols;
	ri->ri_ops.eraserows = gpx_eraserows;
	ri->ri_ops.erasecols = gpx_erasecols;
	ri->ri_ops.putchar = gpx_putchar;
	ri->ri_do_cursor = gpx_do_cursor;

	gpx_stdscreen.ncols = ri->ri_cols;
	gpx_stdscreen.nrows = ri->ri_rows;
	gpx_stdscreen.textops = &ri->ri_ops;
	gpx_stdscreen.fontwidth = ri->ri_font->fontwidth;
	gpx_stdscreen.fontheight = ri->ri_font->fontheight;
	gpx_stdscreen.capabilities = ri->ri_caps;

	/*
	 * Initialize RAMDAC.
	 */
	if (ss->ss_depth == 8) {
		struct ramdac8 *rd = ss->ss_vdac;
		rd->address = BT_CR;
		rd->control = BTCR_RAMENA | BTCR_BLINK_1648 | BTCR_MPLX_4;
	} else {
		struct ramdac4 *rd = ss->ss_vdac;
		rd->control = RAMDAC4_INIT;
	}

	/*
	 * Put the ADDER and VIPER in a good state.
	 */
	gpx_reset_viper(ss);

	/*
	 * Initialize colormap.
	 */
	gpx_resetcmap(ss);

	/*
	 * Clear display (including non-visible area), in 864 lines chunks.
	 */
	gpx_clear_screen(ss);

	/*
	 * Copy our font to the offscreen area.
	 */
	gpx_upload_font(ss);

#if 0
	ss->ss_cursor->cmdr = ss->ss_curcmd = PCCCMD_HSHI;
#endif

	return (0);
}

/*
 * Copy the selected wsfont to non-visible frame buffer area.
 * This is necessary since the only way to send data to the frame buffer
 * is through the ID interface, which is slow and needs 16 bit wide data.
 * Adapted from qdss.
 */
void
gpx_upload_font(struct gpx_screen *ss)
{
	struct rasops_info *ri = &ss->ss_ri;
	struct wsdisplay_font *font = ri->ri_font;
	u_int8_t *fontbits, *fb;
	u_int remaining, nchars, row;
	u_int i, j;
	u_int16_t data;

	/* setup VIPER operand control registers */

	gpx_viper_write(ss, MASK_1, 0xffff);
	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);

	gpx_viper_write(ss, SRC1_OCR_B,
	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
	gpx_viper_write(ss, SRC2_OCR_B,
	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
	gpx_viper_write(ss, DST_OCR_B,
	    EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);

	ss->ss_adder->rasterop_mode =
	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
	gpx_wait(ss, RASTEROP_COMPLETE);

	/*
	 * Load font data. The font is uploaded in 8 or 16 bit wide cells, on
	 * as many ``lines'' as necessary at the end of the display.
	 */
	ss->ss_gpr = MIN(GPX_WIDTH / (NBBY * font->stride), font->numchars);
	if (ss->ss_gpr & 1)
		ss->ss_gpr--;
	fontbits = font->data;
	for (row = 1, remaining = font->numchars; remaining != 0;
	    row++, remaining -= nchars) {
		nchars = MIN(ss->ss_gpr, remaining);

		ss->ss_adder->destination_x = 0;
		ss->ss_adder->destination_y =
		    GPX_HEIGHT - row * font->fontheight;
		ss->ss_adder->fast_dest_dx = nchars * 16;
		ss->ss_adder->slow_dest_dy = font->fontheight;

		/* setup for processor to bitmap xfer */
		gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
		ss->ss_adder->cmd = PBT | OCRB | DTE | LF_R1 | 2; /*XXX why 2?*/

		/* iteratively do the processor to bitmap xfer */
		for (i = font->fontheight; i != 0; i--) {
			fb = fontbits;
			fontbits += font->stride;
			/* PTOB a scan line */
			for (j = nchars; j != 0; j--) {
				/* PTOB one scan of a char cell */
				if (font->stride == 1) {
					data = *fb;
					fb += font->fontheight;
					/*
					 * Do not access past font memory if
					 * it has an odd number of characters
					 * and this is the last pair.
					 */
					if (j != 1 || (nchars & 1) == 0 ||
					    remaining != nchars) {
						data |= ((u_int16_t)*fb) << 8;
						fb += font->fontheight;
					}
				} else {
					data =
					    fb[0] | (((u_int16_t)fb[1]) << 8);
					fb += font->fontheight * font->stride;
				}

				gpx_wait(ss, TX_READY);
				ss->ss_adder->id_data = data;
			}
		}
		fontbits += (nchars - 1) * font->stride * font->fontheight;
	}
}

void
gpx_copyrect(struct gpx_screen *ss,
    int sx, int sy, int dx, int dy, int w, int h)
{
	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff));
	gpx_viper_write(ss, MASK_1, 0xffff);
	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
	gpx_viper_write(ss, SRC1_OCR_B,
	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
	gpx_viper_write(ss, DST_OCR_B,
	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
	ss->ss_adder->fast_dest_dy = 0;
	ss->ss_adder->slow_dest_dx = 0;
	ss->ss_adder->error_1 = 0;
	ss->ss_adder->error_2 = 0;
	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
	gpx_wait(ss, RASTEROP_COMPLETE);
	ss->ss_adder->destination_x = dx;
	ss->ss_adder->fast_dest_dx = w;
	ss->ss_adder->destination_y = dy;
	ss->ss_adder->slow_dest_dy = h;
	ss->ss_adder->source_1_x = sx;
	ss->ss_adder->source_1_dx = w;
	ss->ss_adder->source_1_y = sy;
	ss->ss_adder->source_1_dy = h;
	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
}

/*
 * Fill a rectangle with the given attribute and function (i.e. rop).
 */
void
gpx_fillrect(struct gpx_screen *ss, int x, int y, int dx, int dy, long attr,
    u_int function)
{
	struct rasops_info *ri = &ss->ss_ri;
	int fg, bg;

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

	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff));
	gpx_viper_write(ss, MASK_1, 0xffff);
	gpx_viper_write(ss, SOURCE, 0xffff);
	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
	gpx_viper_write(ss, SRC1_OCR_B,
	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
	gpx_viper_write(ss, DST_OCR_B,
	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
	ss->ss_adder->fast_dest_dx = 0;
	ss->ss_adder->fast_dest_dy = 0;
	ss->ss_adder->slow_dest_dx = 0;
	ss->ss_adder->error_1 = 0;
	ss->ss_adder->error_2 = 0;
	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
	gpx_wait(ss, RASTEROP_COMPLETE);
	ss->ss_adder->destination_x = x;
	ss->ss_adder->fast_dest_dx = dx;
	ss->ss_adder->destination_y = y;
	ss->ss_adder->slow_dest_dy = dy;
	ss->ss_adder->source_1_x = x;
	ss->ss_adder->source_1_dx = dx;
	ss->ss_adder->source_1_y = y;
	ss->ss_adder->source_1_dy = dy;
	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | function;
}

/*
 * Colormap handling routines
 */

int
gpx_getcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count, i;
	u_int colcount = 1 << ss->ss_depth;
	int error;
	u_int8_t ramp[256], *c, *r;

	if (index >= colcount || count > colcount - index)
		return (EINVAL);

	/* extract reds */
	c = ss->ss_cmap + 0 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c << (8 - ss->ss_depth), c += 3;
	if ((error = copyout(ramp, cm->red, count)) != 0)
		return (error);

	/* extract greens */
	c = ss->ss_cmap + 1 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c << (8 - ss->ss_depth), c += 3;
	if ((error = copyout(ramp, cm->green, count)) != 0)
		return (error);

	/* extract blues */
	c = ss->ss_cmap + 2 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c << (8 - ss->ss_depth), c += 3;
	if ((error = copyout(ramp, cm->blue, count)) != 0)
		return (error);

	return (0);
}

int
gpx_putcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count;
	u_int colcount = 1 << ss->ss_depth;
	int i, error;
	u_int8_t r[256], g[256], b[256], *nr, *ng, *nb, *c;

	if (index >= colcount || count > colcount - index)
		return (EINVAL);

	if ((error = copyin(cm->red, r, count)) != 0)
		return (error);
	if ((error = copyin(cm->green, g, count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, b, count)) != 0)
		return (error);

	nr = r, ng = g, nb = b;
	c = ss->ss_cmap + index * 3;
	for (i = count; i != 0; i--) {
		*c++ = *nr++ >> (8 - ss->ss_depth);
		*c++ = *ng++ >> (8 - ss->ss_depth);
		*c++ = *nb++ >> (8 - ss->ss_depth);
	}

	return (0);
}

void
gpx_loadcmap(struct gpx_screen *ss, int from, int count)
{
	u_int8_t *cmap = ss->ss_cmap;
	int i, color12;

	gpx_wait(ss, FRAME_SYNC);
	if (ss->ss_depth == 8) {
		struct ramdac8 *rd = ss->ss_vdac;

		cmap += from * 3;
		rd->address = from;
		for (i = 0; i < count * 3; i++)
			rd->cmapdata = *cmap++;
	} else {
		struct ramdac4 *rd = ss->ss_vdac;

		cmap = ss->ss_cmap + from;
		for (i = from; i < from + count; i++) {
			color12  = (*cmap++ >> 4) << 0;
			color12 |= (*cmap++ >> 4) << 8;
			color12 |= (*cmap++ >> 4) << 4;
			rd->colormap[i] = color12;
		}
	}
}

void
gpx_resetcmap(struct gpx_screen *ss)
{
	if (ss->ss_depth == 8)
		bcopy(rasops_cmap, ss->ss_cmap, sizeof(ss->ss_cmap));
	else {
		bcopy(rasops_cmap, ss->ss_cmap, 8 * 3);
		bcopy(rasops_cmap + 0xf8 * 3, ss->ss_cmap + 8 * 3, 8 * 3);
	}
	gpx_loadcmap(ss, 0, 1 << ss->ss_depth);

	/*
	 * On the 4bit RAMDAC, make the hardware cursor black on black
	 */
	if (ss->ss_depth != 8) {
		struct ramdac4 *rd = ss->ss_vdac;

		rd->cursormap[0] = rd->cursormap[1] =
		    rd->cursormap[2] = rd->cursormap[3] = 0x0000;
	}
}

/*
 * Console support code
 */

int	gpxcnprobe(void);
int	gpxcninit(void);

int
gpxcnprobe()
{
	extern vaddr_t virtual_avail;
	volatile struct adder *adder;
	vaddr_t tmp;
	int depth;
	u_short status;

	switch (vax_boardtype) {
	case VAX_BTYP_410:
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_MULTU)) != 0)
			break; /* doesn't use graphics console */

		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
			break; /* no color option */

		/* Check for hardware */
		tmp = virtual_avail;
		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_ADDER_OFFSET), 1);
		adder = (struct adder *)tmp;
		adder->status = 0;
		status = adder->status;
		iounaccess(tmp, 1);
		if (status == offsetof(struct adder, status))
			return (0);

		/* Check for a recognized color depth */
		tmp = virtual_avail;
		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
		depth = *(u_int16_t *)
		    (tmp + (GPX_READBACK_OFFSET & VAX_PGOFSET)) & 0x00f0;
		iounaccess(tmp, 1);
		if (depth == 0x00f0 || depth == 0x0080)
			return (1);

		break;

	default:
		break;
	}

	return (0);
}

/*
 * Called very early to setup the glass tty as console.
 * Because it's called before the VM system is initialized, virtual memory
 * for the framebuffer can be stolen directly without disturbing anything.
 */
int
gpxcninit()
{
	struct gpx_screen *ss = &gpx_consscr;
	extern vaddr_t virtual_avail;
	vaddr_t ova;
	long defattr;
	struct rasops_info *ri;

	ova = virtual_avail;

	ioaccess(virtual_avail,
	    vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
	ss->ss_depth = (0x00f0 & *(u_int16_t *)(virtual_avail +
	    (GPX_READBACK_OFFSET & VAX_PGOFSET))) == 0x00f0 ? 4 : 8;

	ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1);
	ss->ss_adder = (struct adder *)virtual_avail;
	virtual_avail += VAX_NBPG;

	ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_VDAC_OFFSET), 1);
	ss->ss_vdac = (void *)(virtual_avail + (GPX_VDAC_OFFSET & VAX_PGOFSET));
	virtual_avail += VAX_NBPG;

#if 0
	ioaccess(virtual_avail, GPXADDR + GPX_CURSOR_OFFSET, 1);
	ss->ss_cursor = (struct dc503reg *)virtual_avail;
	virtual_avail += VAX_NBPG;
#endif

	virtual_avail = round_page(virtual_avail);

	/* this had better not fail */
	if (gpx_setup_screen(ss) != 0) {
#if 0
		iounaccess((vaddr_t)ss->ss_cursor, 1);
#endif
		iounaccess((vaddr_t)ss->ss_vdac, 1);
		iounaccess((vaddr_t)ss->ss_adder, 1);
		virtual_avail = ova;
		return (1);
	}

	ri = &ss->ss_ri;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&gpx_stdscreen, ri, 0, 0, defattr);

	return (0);
}


code:
/*	$OpenBSD: hdc9224.c,v 1.42 2013/11/20 00:13:54 dlg Exp $	*/
/*	$NetBSD: hdc9224.c,v 1.16 2001/07/26 15:05:09 wiz Exp $ */
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * All rights reserved.
 *
 * This code is derived from software contributed to Ludd by Bertram Barth.
 *
 * 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 at Ludd, University of
 *	Lule}, Sweden and its contributors.
 * 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.
 */

/*
 * with much help from (in alphabetical order):
 *	Jeremy
 *	Roger Ivie
 *	Rick Macklem
 *	Mike Young
 *
 * Rewritten by Ragge 25 Jun 2000. New features:
 *	- Uses interrupts instead of polling to signal ready.
 *	- Can cooperate with the SCSI routines WRT. the DMA area.
 *
 * TODO:
 *	- Floppy support missing.
 *	- Bad block forwarding missing.
 *	- Statistics collection.
 */
#undef	HDDEBUG

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/dkio.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/syslog.h>
#include <sys/reboot.h>

#include <uvm/uvm_extern.h>

#include <ufs/ufs/dinode.h> /* For BBSIZE */
#include <ufs/ffs/fs.h>

#include <machine/pte.h>
#include <machine/sid.h>
#include <machine/cpu.h>
#include <machine/uvax.h>
#include <machine/ka410.h>
#include <machine/vsbus.h>
#include <machine/scb.h>

#include <arch/vax/mscp/mscp.h> /* For DEC disk encoding */

#include <vax/vsa/hdc9224.h>

/*
 * on-disk geometry block
 */
struct hdgeom {
	char mbz[10];		/* 10 bytes of zero */
	long xbn_count;		/* number of XBNs */
	long dbn_count;		/* number of DBNs */
	long lbn_count;		/* number of LBNs (Logical-Block-Numbers) */
	long rbn_count;		/* number of RBNs (Replacement-Block-Numbers) */
	short nspt;		/* number of sectors per track */
	short ntracks;		/* number of tracks */
	short ncylinders;	/* number of cylinders */
	short precomp;		/* first cylinder for write precompensation */
	short reduced;		/* first cylinder for reduced write current */
	short seek_rate;	/* seek rate or zero for buffered seeks */
	short crc_eec;		/* 0 if CRC, 1 if ECC is being used */
	short rct;		/* "replacement control table" (RCT) */
	short rct_ncopies;	/* number of copies of the RCT */
	long	media_id;	/* media identifier */
	short interleave;	/* sector-to-sector interleave */
	short headskew;		/* head-to-head skew */
	short cylskew;		/* cylinder-to-cylinder skew */
	short gap0_size;	/* size of GAP 0 in the MFM format */
	short gap1_size;	/* size of GAP 1 in the MFM format */
	short gap2_size;	/* size of GAP 2 in the MFM format */
	short gap3_size;	/* size of GAP 3 in the MFM format */
	short sync_value;	/* sync value used when formatting */
	char	reserved[32];	/* reserved for use by the RQDX formatter */
	short serial_number;	/* serial number */
#if 0	/* we don't need these 412 useless bytes ... */
	char	fill[412-2];	/* Filler bytes to the end of the block */
	short checksum;	/* checksum over the XBN */
#endif
} __packed;

/*
 * Software status
 */
struct	hdsoftc {
	struct device sc_dev;		/* must be here! (pseudo-OOP:) */
	struct disk sc_disk;		/* disklabel etc. */
	struct hdgeom sc_xbn;		/* on-disk geometry information */
	int sc_drive;			/* physical unit number */
};

struct	hdcsoftc {
	struct device sc_dev;		/* must be here (pseudo-OOP:) */
	struct evcount sc_intrcnt;
	struct vsbus_dma sc_vd;
	vaddr_t sc_regs;		/* register addresses */
	struct bufq sc_bufq;
	struct buf *sc_active;
	struct hdc9224_UDCreg sc_creg;	/* (command) registers to be written */
	struct hdc9224_UDCreg sc_sreg;	/* (status) registers being read */
	caddr_t	sc_dmabase;		/* */
	int	sc_dmasize;
	caddr_t sc_bufaddr;		/* Current in-core address */
	daddr_t sc_diskblk;		/* Current block on disk */
	int sc_bytecnt;			/* How much left to transfer */
	int sc_xfer;			/* Current transfer size */
	int sc_retries;
	volatile u_char sc_status;	/* last status from interrupt */
	char sc_intbit;
};

struct hdc_attach_args {
	int ha_drive;
};

/*
 * prototypes for (almost) all the internal routines
 */
int hdcmatch(struct device *, void *, void *);
void hdcattach(struct device *, struct device *, void *);
int hdcprint(void *, const char *);
int hdmatch(struct device *, void *, void *);
void hdattach(struct device *, struct device *, void *);
void hdcintr(void *);
int hdc_command(struct hdcsoftc *, int);
void hd_readgeom(struct hdcsoftc *, struct hdsoftc *);
int hdgetdisklabel(dev_t, struct hdsoftc *, struct disklabel *, int);
#ifdef HDDEBUG
void hdc_printgeom(struct hdgeom *);
#endif
void hdc_writeregs(struct hdcsoftc *);
void hdcstart(struct hdcsoftc *, struct buf *);
int hdc_hdselect(struct hdcsoftc *, int);
void hdmakelabel(struct disklabel *, struct hdgeom *);
void hdc_writeregs(struct hdcsoftc *);
void hdc_readregs(struct hdcsoftc *);
void hdc_qstart(void *);

bdev_decl(hd);
cdev_decl(hd);

const struct	cfattach hdc_ca = {
	sizeof(struct hdcsoftc), hdcmatch, hdcattach
};

struct cfdriver hdc_cd = {
	NULL, "hdc", DV_DULL
};

const struct	cfattach hd_ca = {
	sizeof(struct hdsoftc), hdmatch, hdattach
};

struct cfdriver hd_cd = {
	NULL, "hd", DV_DISK
};

/* At least 0.7 uS between register accesses */
static int hd_dmasize, inq = 0;	/* XXX should be in softc... but only 1 ctrl */
static int u;
#define	WAIT	asm("movl %0,%0;movl %0,%0;movl %0,%0; movl %0,%0" :: "m"(u))

#define	HDC_WREG(x)	*(volatile char *)(sc->sc_regs) = (x)
#define	HDC_RREG	*(volatile char *)(sc->sc_regs)
#define	HDC_WCMD(x)	*(volatile char *)(sc->sc_regs + 4) = (x)
#define	HDC_RSTAT	*(volatile char *)(sc->sc_regs + 4)

/*
 * new-config's hdcmatch() is similar to old-config's hdcprobe(),
 * thus we probe for the existence of the controller and reset it.
 * NB: we can't initialize the controller yet, since space for hdcsoftc
 *     is not yet allocated. Thus we do this in hdcattach()...
 */
int
hdcmatch(struct device *parent, void *vcf, void *aux)
{
	static int matched = 0;
	struct vsbus_attach_args *va = aux;
	volatile char *hdc_csr = (char *)va->va_addr;
	int i;

	if (vax_boardtype == VAX_BTYP_49 || vax_boardtype == VAX_BTYP_46 ||
	    vax_boardtype == VAX_BTYP_48 || vax_boardtype == VAX_BTYP_1303)
		return (0);

	/* Can only match once due to DMA setup. This should not be an issue. */
	if (matched != 0)
		return (0);

	hdc_csr[4] = DKC_CMD_RESET; /* reset chip */
	for (i = 0; i < 1000; i++) {
		DELAY(1000);
		if (hdc_csr[4] & DKC_ST_DONE)
			break;
	}
	if (i == 100)
		return 0; /* No response to reset */

	hdc_csr[4] = DKC_CMD_SETREGPTR|UDC_TERM;
	WAIT;
	hdc_csr[0] = UDC_TC_CRCPRE|UDC_TC_INTDONE;
	WAIT;
	hdc_csr[4] = DKC_CMD_DRDESELECT; /* Should be harmless */
	DELAY(1000);
	return (matched = 1);
}

int
hdcprint(void *aux, const char *pnp)
{
	struct hdc_attach_args *ha = aux;

	if (pnp != NULL)
		printf("%s at %s drive %d",
		    ha->ha_drive == 2 ? "ry" : "hd", pnp, ha->ha_drive);

	return (UNCONF);
}

/*
 * hdc_attach() probes for all possible devices
 */
void
hdcattach(struct device *parent, struct device *self, void *aux)
{
	struct vsbus_attach_args *va = aux;
	struct hdcsoftc *sc = (void *)self;
	struct hdc_attach_args ha;
	int status, i;

	u = 0; /* !!! - GCC */

	printf("\n");

	/*
	 * Get interrupt vector, enable instrumentation.
	 */
	scb_vecalloc(va->va_cvec, hdcintr, sc, SCB_ISTACK, &sc->sc_intrcnt);
	evcount_attach(&sc->sc_intrcnt, self->dv_xname, (void *)va->va_cvec);

	sc->sc_regs = vax_map_physmem(va->va_paddr, 1);
	sc->sc_dmabase = (caddr_t)va->va_dmaaddr;
	sc->sc_dmasize = va->va_dmasize;
	sc->sc_intbit = va->va_maskno;
	hd_dmasize = min(MAXPHYS, sc->sc_dmasize); /* Used in hd_minphys */

	sc->sc_vd.vd_go = hdc_qstart;
	sc->sc_vd.vd_arg = sc;

	/*
	 * Reset controller.
	 */
	HDC_WCMD(DKC_CMD_RESET);
	DELAY(1000);
	status = HDC_RSTAT;
	if (status != (DKC_ST_DONE|DKC_TC_SUCCESS)) {
		printf("%s: RESET failed,  status 0x%x\n",
		    sc->sc_dev.dv_xname, status);
		return;
	}

	/*
	 * now probe for all possible hard drives
	 */
	for (i = 0; i < 4; i++) {
		if (i == 2) /* Floppy, needs special handling */
			continue;
		HDC_WCMD(DKC_CMD_DRSELECT | i);
		DELAY(1000);
		status = HDC_RSTAT;
		ha.ha_drive = i;
		if ((status & DKC_ST_TERMCOD) == DKC_TC_SUCCESS)
			config_found(self, (void *)&ha, hdcprint);
	}
}

/*
 * hdmatch() probes for the existence of a HD-type disk/floppy
 */
int
hdmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf;
	void *aux;
{
	struct hdc_attach_args *ha = aux;
	struct cfdata *cf = vcf;

	if (cf->cf_loc[0] != -1 &&
	    cf->cf_loc[0] != ha->ha_drive)
		return 0;

	if (ha->ha_drive == 2) /* Always floppy, not supported */
		return 0;

	return 1;
}

#define	HDMAJOR 19

int
hdgetdisklabel(dev_t dev, struct hdsoftc *hd, struct disklabel *lp,
    int spoofonly)
{
	hdmakelabel(lp, &hd->sc_xbn);

	return readdisklabel(dev, hdstrategy, lp, spoofonly);
}

void
hdattach(struct device *parent, struct device *self, void *aux)
{
	struct hdcsoftc *sc = (void*)parent;
	struct hdsoftc *hd = (void*)self;
	struct hdc_attach_args *ha = aux;
	struct disklabel *dl;
	int error;

	hd->sc_drive = ha->ha_drive;
	/*
	 * Initialize and attach the disk structure.
	 */
	hd->sc_disk.dk_name = hd->sc_dev.dv_xname;
	disk_attach(&hd->sc_dev, &hd->sc_disk);

	/*
	 * if it's not a floppy then evaluate the on-disk geometry.
	 * if necessary correct the label...
	 */
	hd_readgeom(sc, hd);
	disk_printtype(hd->sc_drive, hd->sc_xbn.media_id);
	dl = hd->sc_disk.dk_label;
	error = hdgetdisklabel(MAKEDISKDEV(HDMAJOR, hd->sc_dev.dv_unit, RAW_PART),
	    hd, dl, 0);
	printf("%s: %lluMB, %llu sectors\n",
	    hd->sc_dev.dv_xname, DL_GETDSIZE(dl) / (1048576 / DEV_BSIZE),
	    DL_GETDSIZE(dl));
#ifdef HDDEBUG
	hdc_printgeom(&hd->sc_xbn);
#endif
}

void
hdcintr(void *arg)
{
	struct hdcsoftc *sc = arg;
	struct buf *bp;

	sc->sc_status = HDC_RSTAT;
	if (sc->sc_active == NULL)
		return; /* Complain? */

	if ((sc->sc_status & (DKC_ST_INTPEND | DKC_ST_DONE)) !=
	    (DKC_ST_INTPEND | DKC_ST_DONE))
		return; /* Why spurious ints sometimes??? */

	bp = sc->sc_active;
	sc->sc_active = NULL;
	if ((sc->sc_status & DKC_ST_TERMCOD) != DKC_TC_SUCCESS) {
		int i;
		u_char *g = (u_char *)&sc->sc_sreg;

		if (sc->sc_retries++ < 3) { /* Allow 3 retries */
			hdcstart(sc, bp);
			return;
		}
		printf("%s: failed, status 0x%x\n",
		    sc->sc_dev.dv_xname, sc->sc_status);
		hdc_readregs(sc);
		for (i = 0; i < 10; i++)
			printf("%i: %x\n", i, g[i]);
		bp->b_flags |= B_ERROR;
		bp->b_error = ENXIO;
		bp->b_resid = bp->b_bcount;
		biodone(bp);
		vsbus_dma_intr();
		return;
	}

	if (bp->b_flags & B_READ) {
		vsbus_copytoproc(bp->b_proc, sc->sc_dmabase, sc->sc_bufaddr,
		    sc->sc_xfer);
	}
	sc->sc_diskblk += (sc->sc_xfer/DEV_BSIZE);
	sc->sc_bytecnt -= sc->sc_xfer;
	sc->sc_bufaddr += sc->sc_xfer;

	if (sc->sc_bytecnt == 0) { /* Finished transfer */
		biodone(bp);
		vsbus_dma_intr();
	} else
		hdcstart(sc, bp);
}

/*
 *
 */
void
hdstrategy(struct buf *bp)
{
	struct hdsoftc *hd;
	struct hdcsoftc *sc;
	int unit, s;

	unit = DISKUNIT(bp->b_dev);
	if (unit > hd_cd.cd_ndevs || (hd = hd_cd.cd_devs[unit]) == NULL) {
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		bp->b_resid = bp->b_bcount;
		goto done;
	}
	sc = (void *)hd->sc_dev.dv_parent;

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

	bufq_queue(&sc->sc_bufq, bp);

	s = splbio();
	if (inq == 0) {
		inq = 1;
		vsbus_dma_start(&sc->sc_vd);
	}
	splx(s);
	return;

done:
	s = splbio();
	biodone(bp);
	splx(s);
}

void
hdc_qstart(void *arg)
{
	struct hdcsoftc *sc = arg;

	inq = 0;

	hdcstart(sc, NULL);
	if (bufq_peek(&sc->sc_bufq)) {
		vsbus_dma_start(&sc->sc_vd); /* More to go */
		inq = 1;
	}
}

void
hdcstart(struct hdcsoftc *sc, struct buf *ob)
{
	struct hdc9224_UDCreg *p = &sc->sc_creg;
	struct disklabel *lp;
	struct hdsoftc *hd;
	struct buf *bp;
	int cn, sn, tn, blks;
	volatile char ch;
	daddr_t bn;

	splassert(IPL_BIO);

	if (sc->sc_active != NULL)
		return; /* Already doing something */

	if (ob == NULL) {
		bp = bufq_dequeue(&sc->sc_bufq);
		if (bp == NULL)
			return; /* Nothing to do */

		sc->sc_bufaddr = bp->b_data;
		sc->sc_bytecnt = bp->b_bcount;
		sc->sc_retries = 0;
		bp->b_resid = 0;
	} else
		bp = ob;

	hd = hd_cd.cd_devs[DISKUNIT(bp->b_dev)];
	lp = hd->sc_disk.dk_label;
	hdc_hdselect(sc, hd->sc_drive);
	sc->sc_active = bp;

	if (ob == NULL) {
		sc->sc_diskblk = bp->b_blkno +
		    DL_GETPOFFSET(&lp->d_partitions[DISKPART(bp->b_dev)]);
	}
	bn = sc->sc_diskblk;

        if (bn != 0) {
                cn = bn / lp->d_secpercyl;
                sn = bn % lp->d_secpercyl;
                tn = sn / lp->d_nsectors;
                sn = sn % lp->d_nsectors;
        } else
                cn = sn = tn = 0;

	cn++; /* first cylinder is reserved */

	bzero(p, sizeof(struct hdc9224_UDCreg));

	/*
	 * Tricky thing: the controller itself only increases the sector
	 * number, not the track or cylinder number. Therefore the driver
	 * is not allowed to have transfers that crosses track boundaries.
	 */
	blks = sc->sc_bytecnt / DEV_BSIZE;
	if ((sn + blks) > lp->d_nsectors)
		blks = lp->d_nsectors - sn;

	p->udc_dsect = sn;
	p->udc_dcyl = cn & 0xff;
	p->udc_dhead = ((cn >> 4) & 0x70) | tn;
	p->udc_scnt = blks;

	p->udc_rtcnt = UDC_RC_RTRYCNT;
	p->udc_mode = UDC_MD_HDD;
	p->udc_term = UDC_TC_CRCPRE|UDC_TC_INTDONE|UDC_TC_TDELDAT|UDC_TC_TWRFLT;
	hdc_writeregs(sc);

	/* Count up vars */
	sc->sc_xfer = blks * DEV_BSIZE;

	ch = HDC_RSTAT; /* Avoid pending interrupts */
	WAIT;
	vsbus_clrintr(sc->sc_intbit); /* Clear pending int's */

	if (bp->b_flags & B_READ) {
		HDC_WCMD(DKC_CMD_READ_HDD);
	} else {
		vsbus_copyfromproc(bp->b_proc, sc->sc_bufaddr, sc->sc_dmabase,
		    sc->sc_xfer);
		HDC_WCMD(DKC_CMD_WRITE_HDD);
	}
}

void
hd_readgeom(struct hdcsoftc *sc, struct hdsoftc *hd)
{
	struct hdc9224_UDCreg *p = &sc->sc_creg;

	hdc_hdselect(sc, hd->sc_drive);		/* select drive right now */

	bzero(p, sizeof(struct hdc9224_UDCreg));

	p->udc_scnt  = 1;
	p->udc_rtcnt = UDC_RC_RTRYCNT;
	p->udc_mode  = UDC_MD_HDD;
	p->udc_term  = UDC_TC_CRCPRE|UDC_TC_INTDONE|UDC_TC_TDELDAT|UDC_TC_TWPROT;
	hdc_writeregs(sc);
	sc->sc_status = 0;
	HDC_WCMD(DKC_CMD_READ_HDD|2);
	while ((sc->sc_status & DKC_ST_INTPEND) == 0)
		;
	bcopy(sc->sc_dmabase, &hd->sc_xbn, sizeof(struct hdgeom));
}

#ifdef HDDEBUG
/*
 * display the contents of the on-disk geometry structure
 */
void
hdc_printgeom(p)
	struct hdgeom *p;
{
	printf("**DiskData**	 XBNs: %ld, DBNs: %ld, LBNs: %ld, RBNs: %ld\n",
	    p->xbn_count, p->dbn_count, p->lbn_count, p->rbn_count);
	printf("sec/track: %d, tracks: %d, cyl: %d, precomp/reduced: %d/%d\n",
	    p->nspt, p->ntracks, p->ncylinders, p->precomp, p->reduced);
	printf("seek-rate: %d, crc/eec: %s, RCT: %d, RCT-copies: %d\n",
	    p->seek_rate, p->crc_eec?"EEC":"CRC", p->rct, p->rct_ncopies);
	printf("media-ID: %lx, interleave: %d, headskew: %d, cylskew: %d\n",
	    p->media_id, p->interleave, p->headskew, p->cylskew);
	printf("gap0: %d, gap1: %d, gap2: %d, gap3: %d, sync-value: %d\n",
	    p->gap0_size, p->gap1_size, p->gap2_size, p->gap3_size,
	    p->sync_value);
}
#endif

/*
 * Return the size of a partition, if known, or -1 if not.
 */
daddr_t
hdsize(dev_t dev)
{
	struct hdsoftc *hd;
	int unit = DISKUNIT(dev);
	daddr_t size;

	if (unit >= hd_cd.cd_ndevs || hd_cd.cd_devs[unit] == 0)
		return -1;
	hd = hd_cd.cd_devs[unit];
	size = DL_GETPSIZE(&hd->sc_disk.dk_label->d_partitions[DISKPART(dev)]) *
	    (hd->sc_disk.dk_label->d_secsize / DEV_BSIZE);

	return (size);
}

/*
 *
 */
int
hdopen(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct hdsoftc *hd;
	int unit, part;

	unit = DISKUNIT(dev);
	if (unit >= hd_cd.cd_ndevs)
		return ENXIO;
	hd = hd_cd.cd_devs[unit];
	if (hd == 0)
		return ENXIO;

	part = DISKPART(dev);
	if (part >= hd->sc_disk.dk_label->d_npartitions)
		return ENXIO;

	switch (fmt) {
	case S_IFCHR:
		hd->sc_disk.dk_copenmask |= (1 << part);
		break;
	case S_IFBLK:
		hd->sc_disk.dk_bopenmask |= (1 << part);
		break;
	}
	hd->sc_disk.dk_openmask =
	    hd->sc_disk.dk_copenmask | hd->sc_disk.dk_bopenmask;

	return 0;
}

/*
 *
 */
int
hdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct hdsoftc *hd;
	int part;

	hd = hd_cd.cd_devs[DISKUNIT(dev)];
	part = DISKPART(dev);

	switch (fmt) {
	case S_IFCHR:
		hd->sc_disk.dk_copenmask &= ~(1 << part);
		break;
	case S_IFBLK:
		hd->sc_disk.dk_bopenmask &= ~(1 << part);
		break;
	}
	hd->sc_disk.dk_openmask =
	    hd->sc_disk.dk_copenmask | hd->sc_disk.dk_bopenmask;

	return (0);
}

/*
 *
 */
int
hdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
	struct hdsoftc *hd = hd_cd.cd_devs[DISKUNIT(dev)];
	struct disklabel *lp = hd->sc_disk.dk_label;
	int error = 0;

	switch (cmd) {
	case DIOCGPDINFO:
		hdgetdisklabel(dev, hd, (struct disklabel *)addr, 1);
		break;

	case DIOCGDINFO:
		bcopy(lp, addr, sizeof (struct disklabel));
		break;

	case DIOCGPART:
		((struct partinfo *)addr)->disklab = lp;
		((struct partinfo *)addr)->part =
		  &lp->d_partitions[DISKPART(dev)];
		break;

	case DIOCWDINFO:
	case DIOCSDINFO:
		if ((flag & FWRITE) == 0)
			return EBADF;
		error = setdisklabel(lp, (struct disklabel *)addr, 0);
		if (error == 0) {
			if (cmd == DIOCWDINFO)
				error = writedisklabel(dev, hdstrategy, lp);
		}
		break;

	default:
		error = ENOTTY;
		break;
	}
	return error;
}

/*
 *
 */
int
hdread(dev_t dev, struct uio *uio, int flag)
{
	return (physio(hdstrategy, dev, B_READ, minphys, uio));
}

/*
 *
 */
int
hdwrite(dev_t dev, struct uio *uio, int flag)
{
	return (physio(hdstrategy, dev, B_WRITE, minphys, uio));
}

/*
 *
 */
int
hddump(dev_t dev, daddr_t daddr, caddr_t addr, size_t size)
{
	return 0;
}

/*
 * we have to wait 0.7 usec between two accesses to any of the
 * dkc-registers, on a VS2000 with 1 MIPS, this is roughly one
 * instruction. Thus the loop-overhead will be enough...
 */
void
hdc_readregs(struct hdcsoftc *sc)
{
	int i;
	char *p;

	HDC_WCMD(DKC_CMD_SETREGPTR);
	WAIT;
	p = (void *)&sc->sc_sreg;
	for (i = 0; i < 10; i++) {
		*p++ = HDC_RREG;	/* dkc_reg auto-increments */
		WAIT;
	}
}

void
hdc_writeregs(struct hdcsoftc *sc)
{
	int i;
	char *p;

	HDC_WCMD(DKC_CMD_SETREGPTR);
	p = (void *)&sc->sc_creg;
	for (i = 0; i < 10; i++) {
		HDC_WREG(*p++);	/* dkc_reg auto-increments */
		WAIT;
	}
}

/*
 * hdc_command() issues a command and polls the intreq-register
 * to find when command has completed
 */
int
hdc_command(struct hdcsoftc *sc, int cmd)
{
	hdc_writeregs(sc);		/* write the prepared registers */
	HDC_WCMD(cmd);
	WAIT;
	return (0);
}

int
hdc_hdselect(struct hdcsoftc *sc, int unit)
{
	struct hdc9224_UDCreg *p = &sc->sc_creg;
	int error;

	/*
	 * bring "creg" in some known-to-work state and
	 * select the drive with the DRIVE SELECT command.
	 */
	bzero(p, sizeof(struct hdc9224_UDCreg));

	p->udc_rtcnt = UDC_RC_HDD_READ;
	p->udc_mode  = UDC_MD_HDD;
	p->udc_term  = UDC_TC_HDD;

	error = hdc_command(sc, DKC_CMD_DRSEL_HDD | unit);

	return (error);
}

void
hdmakelabel(struct disklabel *dl, struct hdgeom *g)
{
	int n, p = 0;

	dl->d_bbsize = BBSIZE;
	dl->d_sbsize = SBSIZE;
	dl->d_typename[p++] = MSCP_MID_CHAR(2, g->media_id);
	dl->d_typename[p++] = MSCP_MID_CHAR(1, g->media_id);
	if (MSCP_MID_ECH(0, g->media_id))
		dl->d_typename[p++] = MSCP_MID_CHAR(0, g->media_id);
	n = MSCP_MID_NUM(g->media_id);
	if (n > 99) {
		dl->d_typename[p++] = '1';
		n -= 100;
	}
	if (n > 9) {
		dl->d_typename[p++] = (n / 10) + '0';
		n %= 10;
	}
	dl->d_typename[p++] = n + '0';
	dl->d_typename[p] = 0;
	dl->d_type = DTYPE_MSCP; /* XXX - what to use here??? */
	dl->d_secsize = DEV_BSIZE;

	DL_SETDSIZE(dl, g->lbn_count);
	dl->d_nsectors = g->nspt;
	dl->d_ntracks = g->ntracks;
	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
	dl->d_ncylinders = DL_GETDSIZE(dl) / dl->d_secpercyl;

	dl->d_npartitions = MAXPARTITIONS;
	DL_SETPSIZE(&dl->d_partitions[0], DL_GETDSIZE(dl));
	DL_SETPSIZE(&dl->d_partitions[2], DL_GETDSIZE(dl));

	DL_SETPOFFSET(&dl->d_partitions[0], 0);
	DL_SETPOFFSET(&dl->d_partitions[2], 0);
	dl->d_version = 1;
	dl->d_magic = dl->d_magic2 = DISKMAGIC;
	dl->d_checksum = dkcksum(dl);
}

license: unknown


code:
/*	$OpenBSD: hdc9224.h,v 1.7 2006/11/06 22:16:28 miod Exp $	*/
/*	$NetBSD: hdc9224.h,v 1.5 2003/11/10 08:51:52 wiz Exp $ */
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * All rights reserved.
 *
 * This code is derived from software contributed to Ludd by Bertram Barth.
 *
 * 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 at Ludd, University of
 *	Lule}, Sweden and its contributors.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

struct hdc9224_DKCreg {
	unsigned char dkc_reg;	/* Disk Register Data Access Port (rw)*/
	unsigned char fill[3];	/* bytes are longword aligned */
	unsigned char dkc_cmd;	/* Disk Controller Command Port (wo) */
#define dkc_stat dkc_cmd	/* Interrupt Status Port (ro) */
};

/*
 * definition of some commands (constant bits only, incomplete!)
 */
#define DKC_CMD_RESET		0x00	/* terminate non-data-transfer cmds */
#define DKC_CMD_DRDESELECT	0x01	/* done when no drive is in use */
#define DKC_CMD_SETREGPTR	0x40	/* logically or-ed with reg-number */
#define DKC_CMD_DRSELECT	0x20
#define DKC_CMD_DRSEL_HDD	0x24	/* select HDD, or-ed with unit-numb. */
#define DKC_CMD_DRSEL_RX33	0x28	/* or-ed with unit-number of RX33 */
#define DKC_CMD_DRSEL_RX50	0x2C	/* or-ed with unit-number of RX50 */
#define DKC_CMD_RESTORE		0x02
#define DKC_CMD_STEP		0x04
#define DKC_CMD_STEPIN_FDD	0x04	/* one step inward for floppy */
#define DKC_CMD_STEPOUT_FDD	0x06	/* one step outward (toward cyl #0) */
#define DKC_CMD_POLLDRIVE	0x10
#define DKC_CMD_SEEKREADID	0x50
#define DKC_CMD_FORMATTRACK	0x60
#define DKC_CMD_READTRACK	0x5A
#define DKC_CMD_READPHYSICAL	0x58
#define DKC_CMD_READLOGICAL	0x5C
#define DKC_CMD_READ_HDD	0x5D	/* read-logical, bypass=0, xfer=1 */
#define DKC_CMD_READ_RX33	0x5D	/* ??? */
#define DKC_CMD_WRITEPHYSICAL	0x80
#define DKC_CMD_WRITELOGICAL	0xC0
#define DKC_CMD_WRITE_HDD	0xA0	/* bypass=0, ddmark=0 */
#define DKC_CMD_WRITE_RX33	0xA1	/* precompensation differs... */
#define DKC_CMD_WRITE_RX50	0xA4

/*
 * Definition of bits in the DKC_STAT register
 */
#define DKC_ST_INTPEND	(1<<7)		/* interrupt pending */
#define DKC_ST_DMAREQ	(1<<6)		/* DMA request */
#define DKC_ST_DONE	(1<<5)		/* command done */
#define DKC_ST_TERMCOD	(3<<3)		/* termination code (see below) */
#define DKC_ST_RDYCHNG	(1<<2)		/* ready change */
#define DKC_ST_OVRUN	(1<<1)		/* overrun/underrun */
#define DKC_ST_BADSECT	(1<<0)		/* bad sector */

/*
 * Definition of the termination codes
 */
#define DKC_TC_SUCCESS	(0<<3)		/* Successful completion */
#define DKC_TC_RDIDERR	(1<<3)		/* Error in READ-ID sequence */
#define DKC_TC_VRFYERR	(2<<3)		/* Error in VERIFY sequence */
#define DKC_TC_DATAERR	(3<<3)		/* Error in DATA-TRANSFER seq. */

/*
 * Definitions of delays necessary for floppy-operation
 */
#define DKC_DELAY_MOTOR		500	/* allow 500 ms to reach speed */
#define DKC_DELAY_SELECT	 70	/* 70 ms for data-recovery-circuit */
#define DKC_DELAY_POSITION	 59	/* 59 ms for RX33, 100 ms for RX50 */
#define DKC_DELAY_HEADSET	 18	/* 18 ms when changing head-number */

/*
 * The HDC9224 has 11/15(?) internal registers which are accessible via
 * the Disk-Register-Data-Access-Port DKC_REG
 */
struct hdc9224_UDCreg { /* internal disk controller registers */
	u_char udc_dma7;	/*  0: DMA address bits  0 -  7 */
	u_char udc_dma15;	/*  1: DMA address bits  8 - 15 */
	u_char udc_dma23;	/*  2: DMA address bits 16 - 23 */
	u_char udc_dsect;	/*  3: desired/starting sector number */
#define udc_csect udc_dsect	/*     current sector number */
	u_char udc_dhead;	/*  4: cyl-bits 8-10, desired head number */
#define udc_chead udc_dhead	/*     current head number */
	u_char udc_dcyl;	/*  5: desired cylinder number */
#define udc_ccyl udc_dcyl	/*     current cylinder number */
	u_char udc_scnt;	/*  6: sector count register */
	u_char udc_rtcnt;	/*  7: retry count register */
	u_char udc_mode;	/*  8: operation mode/chip status */
#define udc_cstat udc_mode	/*     chip status register */
	u_char udc_term;	/*  9: termination conditions/drive status */
#define udc_dstat udc_term	/*     drive status register */
	u_char udc_data;	/* 10: data */
};

/* UDC regs */
#define	UDC_TERM	9

/*
 * Definition of bits in the Current-Head register
 */
#define UDC_CH_BADSECT	(1<<7)	/* indicates a bad sector (if bypass=0) */
#define UDC_CH_CYLBITS	(0x70)	/* bits 10-8 of current cylinder number */
#define UDC_CH_HEADNO	(0x0F)	/* current head number */

/*
 * Definition of bits in the Retry-Count register
 */
#define UDC_RC_RTRYCNT	(0xF0)	/* 1's compl. in read-log, 0 all others */
#define UDC_RC_RXDISAB	(1<<3)	/* must/should be 0 for normal operation */
#define UDC_RC_INVRDY	(1<<2)	/* polarity of floppy-status, important! */
#define UDC_RC_MOTOR	(1<<1)	/* turn on floppy-motor, no effect on HDD */
#define UDC_RC_LOSPEED	(1<<0)	/* floppy-speed select, RX33: 0, RX50: 1 */

#define UDC_RC_HDD_READ	 0xF2	/* 0x72 ??? */
#define UDC_RC_HDD_WRT	 0xF2	/* 0xF0 ??? */
#define UDC_RC_RX33READ	 0x76	/* enable retries when reading floppies */
#define UDC_RC_RX33WRT	 0xF6
#define UDC_RC_RX50READ	 0x77	/* enable retries when reading floppies */
#define UDC_RC_RX50WRT	 0xF7

/*
 * Definition of bits in the Operating-Mode register
 */
#define UDC_MD_HDMODE	(1<<7)	/* must be 1 for all FDD and HDD */
#define UDC_MD_CHKCOD	(3<<5)	/* error-check: FDD/CRC: 0, HDD/ECC: 1 */
#define UDC_MD_DENS	(1<<4)	/* density select, must be 0 */
#define UDC_MD_UNUSED	(1<<3)	/* bit 3 is not used and must be 0 */
#define UDC_MD_SRATE	(7<<0)	/* seek step rate */

#define UDC_MD_HDD	 0xC0
#define UDC_MD_RX33	 0x82
#define UDC_MD_RX50	 0x81

/*
 * Definition of bits in the Chip-Status register
 */
#define UDC_CS_RETREQ	(1<<7)	/* retry required */
#define UDC_CS_ECCATT	(1<<6)	/* error correction attempted */
#define UDC_CS_ECCERR	(1<<5)	/* ECC/CRC error */
#define UDC_CS_DELDATA	(1<<4)	/* deleted data mark */
#define UDC_CS_SYNCERR	(1<<3)	/* synchronization error */
#define UDC_CS_COMPERR	(1<<2)	/* compare error */
#define UDC_CS_PRESDRV	(0x3)	/* present drive selected */

/*
 * Definition of bits in the Termination-Conditions register
 */
#define UDC_TC_CRCPRE	(1<<7)	/* CRC register preset, must be 1 */
#define UDC_TC_UNUSED	(1<<6)	/* bit 6 is not used and must be 0 */
#define UDC_TC_INTDONE	(1<<5)	/* interrupt on done */
#define UDC_TC_TDELDAT	(1<<4)	/* terminate on deleted data */
#define UDC_TC_TDSTAT3	(1<<3)	/* terminate on drive status 3 change */
#define UDC_TC_TWPROT	(1<<2)	/* terminate on write-protect (FDD only) */
#define UDC_TC_INTRDCH	(1<<1)	/* interrupt on ready change (FDD only) */
#define UDC_TC_TWRFLT	(1<<0)	/* interrupt on write-fault (HDD only) */

#define UDC_TC_HDD	 0xA5	/* 0xB5 ??? */
#define UDC_TC_FDD	 0xA0	/* 0xAA ??? 0xB4 ??? */

/*
 * Definition of bits in the Disk-Status register
 */
#define UDC_DS_SELACK	(1<<7)	/* select acknowledge (harddisk only!) */
#define UDC_DS_INDEX	(1<<6)	/* index point */
#define UDC_DS_SKCOM	(1<<5)	/* seek complete */
#define UDC_DS_TRK00	(1<<4)	/* track 0 */
#define UDC_DS_DSTAT3	(1<<3)	/* drive status 3 (MBZ) */
#define UDC_DS_WRPROT	(1<<2)	/* write protect (floppy only!) */
#define UDC_DS_READY	(1<<1)	/* drive ready bit */
#define UDC_DS_WRFAULT	(1<<0)	/* write fault */


code:
/*	$OpenBSD: if_le_vsbus.c,v 1.15 2014/12/22 02:26:54 tedu Exp $	*/
/*	$NetBSD: if_le_vsbus.c,v 1.10 2000/06/29 07:14:18 mrg Exp $	*/

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

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

#include "bpfilter.h"

#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/socket.h>
#include <sys/device.h>
#include <sys/reboot.h>

#include <uvm/uvm_extern.h>

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

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

#include <machine/cpu.h>
#include <machine/sid.h>
#include <machine/scb.h>
#include <machine/bus.h>
#include <machine/vsbus.h>

#include <dev/ic/lancereg.h>
#include <dev/ic/lancevar.h>
#include <dev/ic/am7990reg.h>
#include <dev/ic/am7990var.h>

struct le_softc {
	struct	am7990_softc sc_am7990; /* Must be first */
	struct	evcount sc_intrcnt;
	bus_dmamap_t sc_dm;
	volatile u_short *sc_rap;
	volatile u_short *sc_rdp;
};

static	int	le_vsbus_match(struct device *, struct cfdata *, void *);
static	void	le_vsbus_attach(struct device *, struct device *, void *);
static	void	lewrcsr(struct lance_softc *, uint16_t, uint16_t);
static	uint16_t lerdcsr(struct lance_softc *, uint16_t);

struct cfattach le_vsbus_ca = {
	sizeof(struct le_softc), (cfmatch_t)le_vsbus_match, le_vsbus_attach
};

static void
lewrcsr(struct lance_softc *ls, uint16_t port, uint16_t val)
{
	struct le_softc *sc = (void *)ls;

	*sc->sc_rap = port;
	*sc->sc_rdp = val;
}

static uint16_t
lerdcsr(struct lance_softc *ls, uint16_t port)
{
	struct le_softc *sc = (void *)ls;

	*sc->sc_rap = port;
	return *sc->sc_rdp;
}

static int
le_vsbus_match(struct device *parent, struct cfdata *cf, void *aux)
{
	struct vsbus_attach_args *va = aux;
	volatile short *rdp, *rap;
	struct leinit initblock;
	bus_dmamap_t map;
	int i;
	int rv = 0;
	int error;

	if (vax_boardtype == VAX_BTYP_49 || vax_boardtype == VAX_BTYP_1303)
		return 0;

	error = bus_dmamap_create(va->va_dmat, sizeof(initblock), 1,
	    sizeof(initblock), 0, BUS_DMA_NOWAIT, &map);
	if (error) {
		return 0;
	}

	error = bus_dmamap_load(va->va_dmat, map, &initblock,
	    sizeof(initblock), NULL, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
	if (error) {
		bus_dmamap_destroy(va->va_dmat, map);
		return 0;
	}

	memset(&initblock, 0, sizeof(initblock));

	rdp = (short *)va->va_addr;
	rap = rdp + 2;

	/* Make sure the chip is stopped. */
	*rap = LE_CSR0;
	*rdp = LE_C0_STOP;
	DELAY(100);
	*rap = LE_CSR1;
	*rdp = map->dm_segs->ds_addr & 0xffff;
	*rap = LE_CSR2;
	*rdp = (map->dm_segs->ds_addr >> 16) & 0xffff;
	*rap = LE_CSR0;
	*rdp = LE_C0_INIT|LE_C0_INEA;

	/* Wait for initialization to finish. */
	for (i = 100; i >= 0; i--) {
		DELAY(1000);
		/* Should have interrupted by now */
		*rap = LE_CSR0;
		if (*rdp & LE_C0_IDON)
			rv = 1;
	}
	*rap = LE_CSR0;
	*rdp = LE_C0_STOP;

	bus_dmamap_unload(va->va_dmat, map);
	bus_dmamap_destroy(va->va_dmat, map);
	return rv;
}

static void
le_vsbus_attach(struct device *parent, struct device *self, void *aux)
{
	struct vsbus_attach_args *va = aux;
	struct le_softc *lesc = (void *)self;
	struct lance_softc *sc = &lesc->sc_am7990.lsc;
	bus_dma_segment_t seg;
	int *lance_addr;
	int i, err, rseg;
	static int cvec;

	lesc->sc_rdp = (short *)vax_map_physmem(NI_BASE, 1);
	lesc->sc_rap = lesc->sc_rdp + 2;

	/*
	 * MD functions.
	 */
	sc->sc_rdcsr = lerdcsr;
	sc->sc_wrcsr = lewrcsr;
	sc->sc_nocarrier = NULL;

	scb_vecalloc(va->va_cvec, (void (*)(void *))am7990_intr, sc,
	    SCB_ISTACK, &lesc->sc_intrcnt);
	cvec = va->va_cvec;
	evcount_attach(&lesc->sc_intrcnt, self->dv_xname, &cvec);

        /*
         * Allocate a (DMA-safe) block for all descriptors and buffers.
         */

#define ALLOCSIZ (64 * 1024)
        err = bus_dmamem_alloc(va->va_dmat, ALLOCSIZ, NBPG, 0,
            &seg, 1, &rseg, BUS_DMA_NOWAIT);
        if (err) {
                printf(": unable to alloc buffer block: err %d\n", err);
                return;
        }
        err = bus_dmamem_map(va->va_dmat, &seg, rseg, ALLOCSIZ,
            (caddr_t *)&sc->sc_mem, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (err) {
                printf(": unable to map buffer block: err %d\n", err);
                bus_dmamem_free(va->va_dmat, &seg, rseg);
                return;
        }
	err = bus_dmamap_create(va->va_dmat, ALLOCSIZ, rseg, ALLOCSIZ,
	    0, BUS_DMA_NOWAIT, &lesc->sc_dm);
        if (err) {
                printf(": unable to create dma map: err %d\n", err);
                bus_dmamem_free(va->va_dmat, &seg, rseg);
                return;
        }
	err = bus_dmamap_load(va->va_dmat, lesc->sc_dm, sc->sc_mem,
	    ALLOCSIZ, NULL, BUS_DMA_NOWAIT);
        if (err) {
                printf(": unable to load dma map: err %d\n", err);
                bus_dmamap_destroy(va->va_dmat, lesc->sc_dm);
                bus_dmamem_free(va->va_dmat, &seg, rseg);
                return;
        }
	printf(" buf 0x%lx-0x%lx", lesc->sc_dm->dm_segs->ds_addr,
	    lesc->sc_dm->dm_segs->ds_addr + lesc->sc_dm->dm_segs->ds_len - 1);
	sc->sc_addr = lesc->sc_dm->dm_segs->ds_addr & 0xffffff;
	sc->sc_memsize = lesc->sc_dm->dm_segs->ds_len;

	sc->sc_copytodesc = lance_copytobuf_contig;
	sc->sc_copyfromdesc = lance_copyfrombuf_contig;
	sc->sc_copytobuf = lance_copytobuf_contig;
	sc->sc_copyfrombuf = lance_copyfrombuf_contig;
	sc->sc_zerobuf = lance_zerobuf_contig;

#ifdef LEDEBUG
	sc->sc_debug = 1;
#endif
	/*
	 * Get the ethernet address out of rom
	 */
	lance_addr = (int *)vax_map_physmem(NI_ADDR, 1);
	for (i = 0; i < 6; i++)
		sc->sc_arpcom.ac_enaddr[i] = (u_char)lance_addr[i];
	vax_unmap_physmem((vaddr_t)lance_addr, 1);

	/* Prettier printout */
	printf("\n%s", self->dv_xname);

	am7990_config(&lesc->sc_am7990);
}


code:
/*	$OpenBSD: if_ze_vsbus.c,v 1.7 2014/11/05 07:09:04 jsg Exp $	*/
/*      $NetBSD: if_ze_vsbus.c,v 1.5 2000/07/26 21:50:49 matt Exp $ */
/*
 * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed at Ludd, University of
 *      Lule}, Sweden and its contributors.
 * 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.
 */

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/sockio.h>

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

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

#include <machine/bus.h>
#include <machine/vsbus.h>
#include <machine/cpu.h>
#include <machine/scb.h>
#include <machine/sid.h>

#include <arch/vax/if/sgecreg.h>
#include <arch/vax/if/sgecvar.h>

/*
 * Addresses.
 */
#define SGECADDR        0x20008000
#define NISA_ROM        0x27800000
#define	SGECVEC		0x108

static	int	zematch(struct device *, void *, void *);
static	void	zeattach(struct device *, struct device *, void *);

struct	cfattach ze_vsbus_ca = {
	sizeof(struct ze_softc), zematch, zeattach
};

/*
 * Check for present SGEC.
 */
int
zematch(parent, cf, aux)
	struct	device *parent;
	void	*cf;
	void	*aux;
{
	/*
	 * Should some more intelligent checking be done???
	 * Should for sure force an interrupt instead...
	 */
	if (vax_boardtype != VAX_BTYP_49)
		return 0;

	/* Fool the interrupt system. */
	scb_fake(SGECVEC, 0x15);
	return 12;
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */
void
zeattach(parent, self, aux)
	struct	device *parent, *self;
	void	*aux;
{
	struct ze_softc *sc = (struct ze_softc *)self;
	struct vsbus_attach_args *va = aux;
	extern struct vax_bus_dma_tag vax_bus_dma_tag;
	int *ea, i;

	/*
	 * Map in SGEC registers.
	 */
	sc->sc_ioh = vax_map_physmem(SGECADDR, 1);
	sc->sc_iot = 0; /* :-) */
	sc->sc_dmat = &vax_bus_dma_tag;

	sc->sc_intvec = SGECVEC;
	scb_vecalloc(va->va_cvec, (void (*)(void *)) sgec_intr,
	    sc, SCB_ISTACK, &sc->sc_intrcnt);
	evcount_attach(&sc->sc_intrcnt, sc->sc_dev.dv_xname, &sc->sc_intvec);

	/*
	 * Map in, read and release ethernet rom address.
	 */
	ea = (int *)vax_map_physmem(NISA_ROM, 1);
	for (i = 0; i < ETHER_ADDR_LEN; i++)
		sc->sc_ac.ac_enaddr[i] = ea[i] & 0377;
	vax_unmap_physmem((vaddr_t)ea, 1);

	sgec_attach(sc);
}